Commit 8417be173db9c9e7699d3902a35cae01b9683e06
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.
Showing
3 changed files
with
66 additions
and
110 deletions
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& 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& 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& 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& 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<EncryptionParameters> 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& R, int& 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}; | ... | ... |