Commit 8417be173db9c9e7699d3902a35cae01b9683e06

Authored by m-holger
1 parent 34142a6c

Refactor encryption permissions handling by replacing integer-based logic with `…

…std::bitset`, reorganizing related methods, and simplifying access to improve clarity and maintainability.
include/qpdf/QPDF.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 <iostream>
... ... @@ -465,7 +466,7 @@ class QPDF
465 466 V(V),
466 467 R(R),
467 468 Length_bytes(Length_bytes),
468   - P(P),
  469 + P(static_cast<unsigned long long>(P)),
469 470 O(O),
470 471 U(U),
471 472 OE(OE),
... ... @@ -480,6 +481,8 @@ class QPDF
480 481 int getR() const;
481 482 int getLengthBytes() const;
482 483 int getP() const;
  484 + // Bits in P are numbered from 1 as in the PDF spec.
  485 + bool getP(size_t bit) const;
483 486 std::string const& getO() const;
484 487 std::string const& getU() const;
485 488 std::string const& getOE() const;
... ... @@ -487,7 +490,8 @@ class QPDF
487 490 std::string const& getPerms() const;
488 491 std::string const& getId1() const;
489 492 bool getEncryptMetadata() const;
490   -
  493 + // Bits in P are numbered from 1 as in the PDF spec.
  494 + void setP(size_t bit, bool val);
491 495 void setO(std::string const&);
492 496 void setU(std::string const&);
493 497 void setV5EncryptionParameters(
... ... @@ -562,7 +566,7 @@ class QPDF
562 566 int V;
563 567 int R;
564 568 int Length_bytes;
565   - int P;
  569 + std::bitset<32> P{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared.
566 570 std::string O;
567 571 std::string U;
568 572 std::string OE;
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -51,7 +51,21 @@ QPDF::EncryptionData::getLengthBytes() const
51 51 int
52 52 QPDF::EncryptionData::getP() const
53 53 {
54   - return this->P;
  54 + return static_cast<int>(P.to_ullong());
  55 +}
  56 +
  57 +bool
  58 +QPDF::EncryptionData::getP(size_t bit) const
  59 +{
  60 + qpdf_assert_debug(bit);
  61 + return P.test(bit - 1);
  62 +}
  63 +
  64 +bool
  65 +QPDF::EncryptionParameters::P(size_t bit) const
  66 +{
  67 + qpdf_assert_debug(bit);
  68 + return P_.test(bit - 1);
55 69 }
56 70  
57 71 std::string const&
... ... @@ -109,6 +123,13 @@ QPDF::EncryptionData::setU(std::string const&amp; U)
109 123 }
110 124  
111 125 void
  126 +QPDF::EncryptionData::setP(size_t bit, bool val)
  127 +{
  128 + qpdf_assert_debug(bit);
  129 + P.set(bit - 1, val);
  130 +}
  131 +
  132 +void
112 133 QPDF::EncryptionData::setV5EncryptionParameters(
113 134 std::string const& O,
114 135 std::string const& OE,
... ... @@ -782,7 +803,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
782 803 int R = encryption_dict.getKey("/R").getIntValueAsInt();
783 804 std::string O = encryption_dict.getKey("/O").getStringValue();
784 805 std::string U = encryption_dict.getKey("/U").getStringValue();
785   - int P = static_cast<int>(encryption_dict.getKey("/P").getIntValue());
  806 + int p = static_cast<int>(encryption_dict.getKey("/P").getIntValue());
786 807  
787 808 // If supporting new encryption R/V values, remember to update error message inside this if
788 809 // statement.
... ... @@ -792,9 +813,9 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
792 813 " (max 6), V = " + std::to_string(V) + " (max 5)");
793 814 }
794 815  
795   - encryption_P = P;
  816 + P_ = std::bitset<32>(static_cast<unsigned long long>(p));
796 817 encryption_V = V;
797   - encryption_R = R;
  818 + R_ = R;
798 819  
799 820 // OE, UE, and Perms are only present if V >= 5.
800 821 std::string OE;
... ... @@ -891,7 +912,7 @@ QPDF::EncryptionParameters::initialize(QPDF&amp; qpdf)
891 912 }
892 913 }
893 914  
894   - EncryptionData data(V, R, Length / 8, P, O, U, OE, UE, Perms, id1, encrypt_metadata);
  915 + EncryptionData data(V, R, Length / 8, p, O, U, OE, UE, Perms, id1, encrypt_metadata);
895 916 if (qm.provided_password_is_hex_key) {
896 917 // ignore passwords in file
897 918 encryption_key = QUtil::hex_decode(provided_password);
... ... @@ -942,12 +963,7 @@ QPDF::getKeyForObject(std::shared_ptr&lt;EncryptionParameters&gt; encp, QPDFObjGen og,
942 963  
943 964 if (og != encp->cached_key_og) {
944 965 encp->cached_object_encryption_key = compute_data_key(
945   - encp->encryption_key,
946   - og.getObj(),
947   - og.getGen(),
948   - use_aes,
949   - encp->encryption_V,
950   - encp->encryption_R);
  966 + encp->encryption_key, og.getObj(), og.getGen(), use_aes, encp->encryption_V, encp->R());
951 967 encp->cached_key_og = og;
952 968 }
953 969  
... ... @@ -1239,8 +1255,8 @@ QPDF::isEncrypted(int&amp; R, int&amp; P)
1239 1255 if (!m->encp->encrypted) {
1240 1256 return false;
1241 1257 }
1242   - P = m->encp->encryption_P;
1243   - R = m->encp->encryption_R;
  1258 + P = m->encp->P();
  1259 + R = m->encp->R();
1244 1260 return true;
1245 1261 }
1246 1262  
... ... @@ -1256,8 +1272,8 @@ QPDF::isEncrypted(
1256 1272 if (!m->encp->encrypted) {
1257 1273 return false;
1258 1274 }
1259   - P = m->encp->encryption_P;
1260   - R = m->encp->encryption_R;
  1275 + P = m->encp->P();
  1276 + R = m->encp->R();
1261 1277 V = m->encp->encryption_V;
1262 1278 stream_method = m->encp->cf_stream;
1263 1279 string_method = m->encp->cf_string;
... ... @@ -1277,135 +1293,57 @@ QPDF::userPasswordMatched() const
1277 1293 return m->encp->user_password_matched;
1278 1294 }
1279 1295  
1280   -static bool
1281   -is_bit_set(int P, int bit)
1282   -{
1283   - // Bits in P are numbered from 1 in the spec
1284   - return ((P & (1 << (bit - 1))) != 0);
1285   -}
1286   -
1287 1296 bool
1288 1297 QPDF::allowAccessibility()
1289 1298 {
1290   - int R = 0;
1291   - int P = 0;
1292   - bool status = true;
1293   - if (isEncrypted(R, P)) {
1294   - if (R < 3) {
1295   - status = is_bit_set(P, 5);
1296   - } else {
1297   - status = is_bit_set(P, 10);
1298   - }
1299   - }
1300   - return status;
  1299 + return m->encp->R() < 3 ? m->encp->P(5) : m->encp->P(10);
1301 1300 }
1302 1301  
1303 1302 bool
1304 1303 QPDF::allowExtractAll()
1305 1304 {
1306   - int R = 0;
1307   - int P = 0;
1308   - bool status = true;
1309   - if (isEncrypted(R, P)) {
1310   - status = is_bit_set(P, 5);
1311   - }
1312   - return status;
  1305 + return m->encp->P(5);
1313 1306 }
1314 1307  
1315 1308 bool
1316 1309 QPDF::allowPrintLowRes()
1317 1310 {
1318   - int R = 0;
1319   - int P = 0;
1320   - bool status = true;
1321   - if (isEncrypted(R, P)) {
1322   - status = is_bit_set(P, 3);
1323   - }
1324   - return status;
  1311 + return m->encp->P(3);
1325 1312 }
1326 1313  
1327 1314 bool
1328 1315 QPDF::allowPrintHighRes()
1329 1316 {
1330   - int R = 0;
1331   - int P = 0;
1332   - bool status = true;
1333   - if (isEncrypted(R, P)) {
1334   - status = is_bit_set(P, 3);
1335   - if ((R >= 3) && (!is_bit_set(P, 12))) {
1336   - status = false;
1337   - }
1338   - }
1339   - return status;
  1317 + return allowPrintLowRes() && (m->encp->R() < 3 ? true : m->encp->P(12));
1340 1318 }
1341 1319  
1342 1320 bool
1343 1321 QPDF::allowModifyAssembly()
1344 1322 {
1345   - int R = 0;
1346   - int P = 0;
1347   - bool status = true;
1348   - if (isEncrypted(R, P)) {
1349   - if (R < 3) {
1350   - status = is_bit_set(P, 4);
1351   - } else {
1352   - status = is_bit_set(P, 11);
1353   - }
1354   - }
1355   - return status;
  1323 + return m->encp->R() < 3 ? m->encp->P(4) : m->encp->P(11);
1356 1324 }
1357 1325  
1358 1326 bool
1359 1327 QPDF::allowModifyForm()
1360 1328 {
1361   - int R = 0;
1362   - int P = 0;
1363   - bool status = true;
1364   - if (isEncrypted(R, P)) {
1365   - if (R < 3) {
1366   - status = is_bit_set(P, 6);
1367   - } else {
1368   - status = is_bit_set(P, 9);
1369   - }
1370   - }
1371   - return status;
  1329 + return m->encp->R() < 3 ? m->encp->P(6) : m->encp->P(9);
1372 1330 }
1373 1331  
1374 1332 bool
1375 1333 QPDF::allowModifyAnnotation()
1376 1334 {
1377   - int R = 0;
1378   - int P = 0;
1379   - bool status = true;
1380   - if (isEncrypted(R, P)) {
1381   - status = is_bit_set(P, 6);
1382   - }
1383   - return status;
  1335 + return m->encp->P(6);
1384 1336 }
1385 1337  
1386 1338 bool
1387 1339 QPDF::allowModifyOther()
1388 1340 {
1389   - int R = 0;
1390   - int P = 0;
1391   - bool status = true;
1392   - if (isEncrypted(R, P)) {
1393   - status = is_bit_set(P, 4);
1394   - }
1395   - return status;
  1341 + return m->encp->P(4);
1396 1342 }
1397 1343  
1398 1344 bool
1399 1345 QPDF::allowModifyAll()
1400 1346 {
1401   - int R = 0;
1402   - int P = 0;
1403   - bool status = true;
1404   - if (isEncrypted(R, P)) {
1405   - status = (is_bit_set(P, 4) && is_bit_set(P, 6));
1406   - if (R >= 3) {
1407   - status = status && (is_bit_set(P, 9) && is_bit_set(P, 11));
1408   - }
1409   - }
1410   - return status;
  1347 + return allowModifyAnnotation() && allowModifyOther() &&
  1348 + (m->encp->R() < 3 ? true : allowModifyForm() && allowModifyAssembly());
1411 1349 }
... ...
libqpdf/qpdf/QPDF_private.hh
... ... @@ -187,16 +187,30 @@ class QPDF::EncryptionParameters
187 187 public:
188 188 EncryptionParameters() = default;
189 189  
190   - encryption_method_e interpretCF(QPDFObjectHandle const& cf) const;
  190 + int
  191 + P() const
  192 + {
  193 + return static_cast<int>(P_.to_ullong());
  194 + }
  195 +
  196 + // Bits in P are numbered from 1 as in the PDF spec.
  197 + bool P(size_t bit) const;
  198 +
  199 + int
  200 + R()
  201 + {
  202 + return R_;
  203 + }
191 204  
192   - private:
193 205 void initialize(QPDF& qpdf);
  206 + encryption_method_e interpretCF(QPDFObjectHandle const& cf) const;
194 207  
  208 + private:
195 209 bool encrypted{false};
196 210 bool encryption_initialized{false};
197   - int encryption_P{0};
  211 + std::bitset<32> P_{0xfffffffc}; // Specification always requires bits 1 and 2 to be cleared.
198 212 int encryption_V{0};
199   - int encryption_R{0};
  213 + int R_{0};
200 214 bool encrypt_metadata{true};
201 215 std::map<std::string, encryption_method_e> crypt_filters;
202 216 encryption_method_e cf_stream{e_none};
... ...