Commit cb3c4c55d4cd721f647fa544a98bc0cb24c97823
1 parent
34f52682
Refactor `QPDF::EncryptionData` to encapsulate encryption logic, reduce redundan…
…t parameter passing, and improve maintainability. Change static functions and QPDF methods to EncryptionData methods.
Showing
2 changed files
with
218 additions
and
158 deletions
include/qpdf/QPDF.hh
| @@ -497,10 +497,68 @@ class QPDF | @@ -497,10 +497,68 @@ class QPDF | ||
| 497 | std::string const& UE, | 497 | std::string const& UE, |
| 498 | std::string const& Perms); | 498 | std::string const& Perms); |
| 499 | 499 | ||
| 500 | + std::string compute_encryption_key(std::string const& password) const; | ||
| 501 | + | ||
| 502 | + bool | ||
| 503 | + check_owner_password(std::string& user_password, std::string const& owner_password) const; | ||
| 504 | + | ||
| 505 | + bool check_user_password(std::string const& user_password) const; | ||
| 506 | + | ||
| 507 | + std::string | ||
| 508 | + recover_encryption_key_with_password(std::string const& password, bool& perms_valid) const; | ||
| 509 | + | ||
| 510 | + void compute_encryption_O_U( | ||
| 511 | + char const* user_password, char const* owner_password, std::string& O, std::string& U); | ||
| 512 | + | ||
| 513 | + void compute_encryption_parameters_V5( | ||
| 514 | + char const* user_password, | ||
| 515 | + char const* owner_password, | ||
| 516 | + std::string& encryption_key, | ||
| 517 | + std::string& O, | ||
| 518 | + std::string& U, | ||
| 519 | + std::string& OE, | ||
| 520 | + std::string& UE, | ||
| 521 | + std::string& Perms); | ||
| 522 | + | ||
| 500 | private: | 523 | private: |
| 524 | + static constexpr unsigned int OU_key_bytes_V4 = 16; // ( == sizeof(MD5::Digest) | ||
| 525 | + | ||
| 501 | EncryptionData(EncryptionData const&) = delete; | 526 | EncryptionData(EncryptionData const&) = delete; |
| 502 | EncryptionData& operator=(EncryptionData const&) = delete; | 527 | EncryptionData& operator=(EncryptionData const&) = delete; |
| 503 | 528 | ||
| 529 | + std::string hash_V5( | ||
| 530 | + std::string const& password, std::string const& salt, std::string const& udata) const; | ||
| 531 | + std::string compute_Perms_value_V5(std::string const& encryption_key) const; | ||
| 532 | + std::string | ||
| 533 | + compute_O_value(std::string const& user_password, std::string const& owner_password) const; | ||
| 534 | + std::string compute_U_value(std::string const& user_password) const; | ||
| 535 | + void compute_O_OE_value_V5( | ||
| 536 | + std::string const& owner_password, | ||
| 537 | + std::string const& encryption_key, | ||
| 538 | + std::string const& U, | ||
| 539 | + std::string& O, | ||
| 540 | + std::string& OE) const; | ||
| 541 | + void compute_U_UE_value_V5( | ||
| 542 | + std::string const& user_password, | ||
| 543 | + std::string const& encryption_key, | ||
| 544 | + std::string& U, | ||
| 545 | + std::string& UE) const; | ||
| 546 | + std::string compute_encryption_key_from_password(std::string const& password) const; | ||
| 547 | + std::string recover_encryption_key_with_password(std::string const& password) const; | ||
| 548 | + bool check_owner_password_V4( | ||
| 549 | + std::string& user_password, std::string const& owner_password) const; | ||
| 550 | + bool check_owner_password_V5(std::string const& owner_passworda) const; | ||
| 551 | + void | ||
| 552 | + compute_Perms_value_V5_clear(std::string const& encryption_key, unsigned char k[16]) const; | ||
| 553 | + void compute_O_rc4_key( | ||
| 554 | + std::string const& user_password, | ||
| 555 | + std::string const& owner_password, | ||
| 556 | + unsigned char key[OU_key_bytes_V4]) const; | ||
| 557 | + std::string compute_U_value_R2(std::string const& user_password) const; | ||
| 558 | + std::string compute_U_value_R3(std::string const& user_password) const; | ||
| 559 | + bool check_user_password_V4(std::string const& user_password) const; | ||
| 560 | + bool check_user_password_V5(std::string const& user_password) const; | ||
| 561 | + | ||
| 504 | int V; | 562 | int V; |
| 505 | int R; | 563 | int R; |
| 506 | int Length_bytes; | 564 | int Length_bytes; |
| @@ -912,12 +970,6 @@ class QPDF | @@ -912,12 +970,6 @@ class QPDF | ||
| 912 | static std::string | 970 | static std::string |
| 913 | getKeyForObject(std::shared_ptr<EncryptionParameters> encp, QPDFObjGen og, bool use_aes); | 971 | getKeyForObject(std::shared_ptr<EncryptionParameters> encp, QPDFObjGen og, bool use_aes); |
| 914 | void decryptString(std::string&, QPDFObjGen og); | 972 | void decryptString(std::string&, QPDFObjGen og); |
| 915 | - static std::string | ||
| 916 | - compute_encryption_key_from_password(std::string const& password, EncryptionData const& data); | ||
| 917 | - static std::string | ||
| 918 | - recover_encryption_key_with_password(std::string const& password, EncryptionData const& data); | ||
| 919 | - static std::string recover_encryption_key_with_password( | ||
| 920 | - std::string const& password, EncryptionData const& data, bool& perms_valid); | ||
| 921 | static void decryptStream( | 973 | static void decryptStream( |
| 922 | std::shared_ptr<EncryptionParameters> encp, | 974 | std::shared_ptr<EncryptionParameters> encp, |
| 923 | std::shared_ptr<InputSource> file, | 975 | std::shared_ptr<InputSource> file, |
libqpdf/QPDF_encryption.cc
| @@ -25,9 +25,6 @@ static unsigned char const padding_string[] = { | @@ -25,9 +25,6 @@ static unsigned char const padding_string[] = { | ||
| 25 | 25 | ||
| 26 | static unsigned int const key_bytes = 32; | 26 | static unsigned int const key_bytes = 32; |
| 27 | 27 | ||
| 28 | -// V4 key lengths apply to V <= 4 | ||
| 29 | -static unsigned int const OU_key_bytes_V4 = sizeof(MD5::Digest); | ||
| 30 | - | ||
| 31 | static unsigned int const OU_key_bytes_V5 = 48; | 28 | static unsigned int const OU_key_bytes_V5 = 48; |
| 32 | static unsigned int const OUE_key_bytes_V5 = 32; | 29 | static unsigned int const OUE_key_bytes_V5 = 32; |
| 33 | static unsigned int const Perms_key_bytes_V5 = 16; | 30 | static unsigned int const Perms_key_bytes_V5 = 16; |
| @@ -236,12 +233,9 @@ process_with_aes( | @@ -236,12 +233,9 @@ process_with_aes( | ||
| 236 | } | 233 | } |
| 237 | } | 234 | } |
| 238 | 235 | ||
| 239 | -static std::string | ||
| 240 | -hash_V5( | ||
| 241 | - std::string const& password, | ||
| 242 | - std::string const& salt, | ||
| 243 | - std::string const& udata, | ||
| 244 | - QPDF::EncryptionData const& data) | 236 | +std::string |
| 237 | +QPDF::EncryptionData::hash_V5( | ||
| 238 | + std::string const& password, std::string const& salt, std::string const& udata) const | ||
| 245 | { | 239 | { |
| 246 | Pl_SHA2 hash(256); | 240 | Pl_SHA2 hash(256); |
| 247 | hash.writeString(password); | 241 | hash.writeString(password); |
| @@ -251,7 +245,7 @@ hash_V5( | @@ -251,7 +245,7 @@ hash_V5( | ||
| 251 | std::string K = hash.getRawDigest(); | 245 | std::string K = hash.getRawDigest(); |
| 252 | 246 | ||
| 253 | std::string result; | 247 | std::string result; |
| 254 | - if (data.getR() < 6) { | 248 | + if (getR() < 6) { |
| 255 | result = K; | 249 | result = K; |
| 256 | } else { | 250 | } else { |
| 257 | // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash | 251 | // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash |
| @@ -359,19 +353,25 @@ QPDF::compute_data_key( | @@ -359,19 +353,25 @@ QPDF::compute_data_key( | ||
| 359 | std::string | 353 | std::string |
| 360 | QPDF::compute_encryption_key(std::string const& password, EncryptionData const& data) | 354 | QPDF::compute_encryption_key(std::string const& password, EncryptionData const& data) |
| 361 | { | 355 | { |
| 362 | - if (data.getV() >= 5) { | 356 | + return data.compute_encryption_key(password); |
| 357 | +} | ||
| 358 | + | ||
| 359 | +std::string | ||
| 360 | +QPDF::EncryptionData::compute_encryption_key(std::string const& password) const | ||
| 361 | +{ | ||
| 362 | + if (getV() >= 5) { | ||
| 363 | // For V >= 5, the encryption key is generated and stored in the file, encrypted separately | 363 | // For V >= 5, the encryption key is generated and stored in the file, encrypted separately |
| 364 | // with both user and owner passwords. | 364 | // with both user and owner passwords. |
| 365 | - return recover_encryption_key_with_password(password, data); | 365 | + return recover_encryption_key_with_password(password); |
| 366 | } else { | 366 | } else { |
| 367 | // For V < 5, the encryption key is derived from the user | 367 | // For V < 5, the encryption key is derived from the user |
| 368 | // password. | 368 | // password. |
| 369 | - return compute_encryption_key_from_password(password, data); | 369 | + return compute_encryption_key_from_password(password); |
| 370 | } | 370 | } |
| 371 | } | 371 | } |
| 372 | 372 | ||
| 373 | std::string | 373 | std::string |
| 374 | -QPDF::compute_encryption_key_from_password(std::string const& password, EncryptionData const& data) | 374 | +QPDF::EncryptionData::compute_encryption_key_from_password(std::string const& password) const |
| 375 | { | 375 | { |
| 376 | // Algorithm 3.2 from the PDF 1.7 Reference Manual | 376 | // Algorithm 3.2 from the PDF 1.7 Reference Manual |
| 377 | 377 | ||
| @@ -382,34 +382,33 @@ QPDF::compute_encryption_key_from_password(std::string const& password, Encrypti | @@ -382,34 +382,33 @@ QPDF::compute_encryption_key_from_password(std::string const& password, Encrypti | ||
| 382 | 382 | ||
| 383 | MD5 md5; | 383 | MD5 md5; |
| 384 | md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes); | 384 | md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes); |
| 385 | - md5.encodeDataIncrementally(data.getO().c_str(), key_bytes); | 385 | + md5.encodeDataIncrementally(getO().c_str(), key_bytes); |
| 386 | char pbytes[4]; | 386 | char pbytes[4]; |
| 387 | - int P = data.getP(); | 387 | + int P = getP(); |
| 388 | pbytes[0] = static_cast<char>(P & 0xff); | 388 | pbytes[0] = static_cast<char>(P & 0xff); |
| 389 | pbytes[1] = static_cast<char>((P >> 8) & 0xff); | 389 | pbytes[1] = static_cast<char>((P >> 8) & 0xff); |
| 390 | pbytes[2] = static_cast<char>((P >> 16) & 0xff); | 390 | pbytes[2] = static_cast<char>((P >> 16) & 0xff); |
| 391 | pbytes[3] = static_cast<char>((P >> 24) & 0xff); | 391 | pbytes[3] = static_cast<char>((P >> 24) & 0xff); |
| 392 | md5.encodeDataIncrementally(pbytes, 4); | 392 | md5.encodeDataIncrementally(pbytes, 4); |
| 393 | - md5.encodeDataIncrementally(data.getId1().c_str(), data.getId1().length()); | ||
| 394 | - if ((data.getR() >= 4) && (!data.getEncryptMetadata())) { | 393 | + md5.encodeDataIncrementally(getId1().c_str(), getId1().length()); |
| 394 | + if (getR() >= 4 && !getEncryptMetadata()) { | ||
| 395 | char bytes[4]; | 395 | char bytes[4]; |
| 396 | memset(bytes, 0xff, 4); | 396 | memset(bytes, 0xff, 4); |
| 397 | md5.encodeDataIncrementally(bytes, 4); | 397 | md5.encodeDataIncrementally(bytes, 4); |
| 398 | } | 398 | } |
| 399 | MD5::Digest digest; | 399 | MD5::Digest digest; |
| 400 | - int key_len = std::min(toI(sizeof(digest)), data.getLengthBytes()); | ||
| 401 | - iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0), key_len); | 400 | + int key_len = std::min(toI(sizeof(digest)), getLengthBytes()); |
| 401 | + iterate_md5_digest(md5, digest, (getR() >= 3 ? 50 : 0), key_len); | ||
| 402 | return {reinterpret_cast<char*>(digest), toS(key_len)}; | 402 | return {reinterpret_cast<char*>(digest), toS(key_len)}; |
| 403 | } | 403 | } |
| 404 | 404 | ||
| 405 | -static void | ||
| 406 | -compute_O_rc4_key( | 405 | +void |
| 406 | +QPDF::EncryptionData::compute_O_rc4_key( | ||
| 407 | std::string const& user_password, | 407 | std::string const& user_password, |
| 408 | std::string const& owner_password, | 408 | std::string const& owner_password, |
| 409 | - QPDF::EncryptionData const& data, | ||
| 410 | - unsigned char key[OU_key_bytes_V4]) | 409 | + unsigned char key[OU_key_bytes_V4]) const |
| 411 | { | 410 | { |
| 412 | - if (data.getV() >= 5) { | 411 | + if (getV() >= 5) { |
| 413 | throw std::logic_error("compute_O_rc4_key called for file with V >= 5"); | 412 | throw std::logic_error("compute_O_rc4_key called for file with V >= 5"); |
| 414 | } | 413 | } |
| 415 | std::string password = owner_password; | 414 | std::string password = owner_password; |
| @@ -419,252 +418,241 @@ compute_O_rc4_key( | @@ -419,252 +418,241 @@ compute_O_rc4_key( | ||
| 419 | MD5 md5; | 418 | MD5 md5; |
| 420 | md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes); | 419 | md5.encodeDataIncrementally(pad_or_truncate_password_V4(password).c_str(), key_bytes); |
| 421 | MD5::Digest digest; | 420 | MD5::Digest digest; |
| 422 | - int key_len = std::min(QIntC::to_int(sizeof(digest)), data.getLengthBytes()); | ||
| 423 | - iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0), key_len); | 421 | + int key_len = std::min(QIntC::to_int(sizeof(digest)), getLengthBytes()); |
| 422 | + iterate_md5_digest(md5, digest, (getR() >= 3 ? 50 : 0), key_len); | ||
| 424 | memcpy(key, digest, OU_key_bytes_V4); | 423 | memcpy(key, digest, OU_key_bytes_V4); |
| 425 | } | 424 | } |
| 426 | 425 | ||
| 427 | -static std::string | ||
| 428 | -compute_O_value( | ||
| 429 | - std::string const& user_password, | ||
| 430 | - std::string const& owner_password, | ||
| 431 | - QPDF::EncryptionData const& data) | 426 | +std::string |
| 427 | +QPDF::EncryptionData::compute_O_value( | ||
| 428 | + std::string const& user_password, std::string const& owner_password) const | ||
| 432 | { | 429 | { |
| 433 | // Algorithm 3.3 from the PDF 1.7 Reference Manual | 430 | // Algorithm 3.3 from the PDF 1.7 Reference Manual |
| 434 | 431 | ||
| 435 | unsigned char O_key[OU_key_bytes_V4]; | 432 | unsigned char O_key[OU_key_bytes_V4]; |
| 436 | - compute_O_rc4_key(user_password, owner_password, data, O_key); | 433 | + compute_O_rc4_key(user_password, owner_password, O_key); |
| 437 | 434 | ||
| 438 | char upass[key_bytes]; | 435 | char upass[key_bytes]; |
| 439 | pad_or_truncate_password_V4(user_password, upass); | 436 | pad_or_truncate_password_V4(user_password, upass); |
| 440 | std::string k1(reinterpret_cast<char*>(O_key), OU_key_bytes_V4); | 437 | std::string k1(reinterpret_cast<char*>(O_key), OU_key_bytes_V4); |
| 441 | - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes())); | 438 | + pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 442 | iterate_rc4( | 439 | iterate_rc4( |
| 443 | QUtil::unsigned_char_pointer(upass), | 440 | QUtil::unsigned_char_pointer(upass), |
| 444 | key_bytes, | 441 | key_bytes, |
| 445 | O_key, | 442 | O_key, |
| 446 | - data.getLengthBytes(), | ||
| 447 | - (data.getR() >= 3) ? 20 : 1, | 443 | + getLengthBytes(), |
| 444 | + getR() >= 3 ? 20 : 1, | ||
| 448 | false); | 445 | false); |
| 449 | return {upass, key_bytes}; | 446 | return {upass, key_bytes}; |
| 450 | } | 447 | } |
| 451 | 448 | ||
| 452 | -static std::string | ||
| 453 | -compute_U_value_R2(std::string const& user_password, QPDF::EncryptionData const& data) | 449 | +std::string |
| 450 | +QPDF::EncryptionData::compute_U_value_R2(std::string const& user_password) const | ||
| 454 | { | 451 | { |
| 455 | // Algorithm 3.4 from the PDF 1.7 Reference Manual | 452 | // Algorithm 3.4 from the PDF 1.7 Reference Manual |
| 456 | 453 | ||
| 457 | - std::string k1 = QPDF::compute_encryption_key(user_password, data); | 454 | + std::string k1 = compute_encryption_key(user_password); |
| 458 | char udata[key_bytes]; | 455 | char udata[key_bytes]; |
| 459 | pad_or_truncate_password_V4("", udata); | 456 | pad_or_truncate_password_V4("", udata); |
| 460 | - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes())); | 457 | + pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 461 | iterate_rc4( | 458 | iterate_rc4( |
| 462 | QUtil::unsigned_char_pointer(udata), | 459 | QUtil::unsigned_char_pointer(udata), |
| 463 | key_bytes, | 460 | key_bytes, |
| 464 | QUtil::unsigned_char_pointer(k1), | 461 | QUtil::unsigned_char_pointer(k1), |
| 465 | - data.getLengthBytes(), | 462 | + getLengthBytes(), |
| 466 | 1, | 463 | 1, |
| 467 | false); | 464 | false); |
| 468 | return {udata, key_bytes}; | 465 | return {udata, key_bytes}; |
| 469 | } | 466 | } |
| 470 | 467 | ||
| 471 | -static std::string | ||
| 472 | -compute_U_value_R3(std::string const& user_password, QPDF::EncryptionData const& data) | 468 | +std::string |
| 469 | +QPDF::EncryptionData::compute_U_value_R3(std::string const& user_password) const | ||
| 473 | { | 470 | { |
| 474 | // Algorithm 3.5 from the PDF 1.7 Reference Manual | 471 | // Algorithm 3.5 from the PDF 1.7 Reference Manual |
| 475 | 472 | ||
| 476 | - std::string k1 = QPDF::compute_encryption_key(user_password, data); | 473 | + std::string k1 = compute_encryption_key(user_password); |
| 477 | MD5 md5; | 474 | MD5 md5; |
| 478 | md5.encodeDataIncrementally(pad_or_truncate_password_V4("").c_str(), key_bytes); | 475 | md5.encodeDataIncrementally(pad_or_truncate_password_V4("").c_str(), key_bytes); |
| 479 | - md5.encodeDataIncrementally(data.getId1().c_str(), data.getId1().length()); | 476 | + md5.encodeDataIncrementally(getId1().c_str(), getId1().length()); |
| 480 | MD5::Digest digest; | 477 | MD5::Digest digest; |
| 481 | md5.digest(digest); | 478 | md5.digest(digest); |
| 482 | - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes())); | 479 | + pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 483 | iterate_rc4( | 480 | iterate_rc4( |
| 484 | - digest, | ||
| 485 | - sizeof(MD5::Digest), | ||
| 486 | - QUtil::unsigned_char_pointer(k1), | ||
| 487 | - data.getLengthBytes(), | ||
| 488 | - 20, | ||
| 489 | - false); | 481 | + digest, sizeof(MD5::Digest), QUtil::unsigned_char_pointer(k1), getLengthBytes(), 20, false); |
| 490 | char result[key_bytes]; | 482 | char result[key_bytes]; |
| 491 | memcpy(result, digest, sizeof(MD5::Digest)); | 483 | memcpy(result, digest, sizeof(MD5::Digest)); |
| 492 | - // pad with arbitrary data -- make it consistent for the sake of | ||
| 493 | - // testing | 484 | + // pad with arbitrary data -- make it consistent for the sake of testing |
| 494 | for (unsigned int i = sizeof(MD5::Digest); i < key_bytes; ++i) { | 485 | for (unsigned int i = sizeof(MD5::Digest); i < key_bytes; ++i) { |
| 495 | result[i] = static_cast<char>((i * i) % 0xff); | 486 | result[i] = static_cast<char>((i * i) % 0xff); |
| 496 | } | 487 | } |
| 497 | return {result, key_bytes}; | 488 | return {result, key_bytes}; |
| 498 | } | 489 | } |
| 499 | 490 | ||
| 500 | -static std::string | ||
| 501 | -compute_U_value(std::string const& user_password, QPDF::EncryptionData const& data) | 491 | +std::string |
| 492 | +QPDF::EncryptionData::compute_U_value(std::string const& user_password) const | ||
| 502 | { | 493 | { |
| 503 | - if (data.getR() >= 3) { | ||
| 504 | - return compute_U_value_R3(user_password, data); | 494 | + if (getR() >= 3) { |
| 495 | + return compute_U_value_R3(user_password); | ||
| 505 | } | 496 | } |
| 506 | 497 | ||
| 507 | - return compute_U_value_R2(user_password, data); | 498 | + return compute_U_value_R2(user_password); |
| 508 | } | 499 | } |
| 509 | 500 | ||
| 510 | -static bool | ||
| 511 | -check_user_password_V4(std::string const& user_password, QPDF::EncryptionData const& data) | 501 | +bool |
| 502 | +QPDF::EncryptionData::check_user_password_V4(std::string const& user_password) const | ||
| 512 | { | 503 | { |
| 513 | // Algorithm 3.6 from the PDF 1.7 Reference Manual | 504 | // Algorithm 3.6 from the PDF 1.7 Reference Manual |
| 514 | 505 | ||
| 515 | - std::string u_value = compute_U_value(user_password, data); | ||
| 516 | - size_t to_compare = ((data.getR() >= 3) ? sizeof(MD5::Digest) : key_bytes); | ||
| 517 | - return (memcmp(data.getU().c_str(), u_value.c_str(), to_compare) == 0); | 506 | + std::string u_value = compute_U_value(user_password); |
| 507 | + size_t to_compare = (getR() >= 3 ? sizeof(MD5::Digest) : key_bytes); | ||
| 508 | + return memcmp(getU().c_str(), u_value.c_str(), to_compare) == 0; | ||
| 518 | } | 509 | } |
| 519 | 510 | ||
| 520 | -static bool | ||
| 521 | -check_user_password_V5(std::string const& user_password, QPDF::EncryptionData const& data) | 511 | +bool |
| 512 | +QPDF::EncryptionData::check_user_password_V5(std::string const& user_password) const | ||
| 522 | { | 513 | { |
| 523 | // Algorithm 3.11 from the PDF 1.7 extension level 3 | 514 | // Algorithm 3.11 from the PDF 1.7 extension level 3 |
| 524 | 515 | ||
| 525 | - std::string user_data = data.getU().substr(0, 32); | ||
| 526 | - std::string validation_salt = data.getU().substr(32, 8); | 516 | + std::string user_data = getU().substr(0, 32); |
| 517 | + std::string validation_salt = getU().substr(32, 8); | ||
| 527 | std::string password = truncate_password_V5(user_password); | 518 | std::string password = truncate_password_V5(user_password); |
| 528 | - return (hash_V5(password, validation_salt, "", data) == user_data); | 519 | + return hash_V5(password, validation_salt, "") == user_data; |
| 529 | } | 520 | } |
| 530 | 521 | ||
| 531 | -static bool | ||
| 532 | -check_user_password(std::string const& user_password, QPDF::EncryptionData const& data) | 522 | +bool |
| 523 | +QPDF::EncryptionData::check_user_password(std::string const& user_password) const | ||
| 533 | { | 524 | { |
| 534 | - if (data.getV() < 5) { | ||
| 535 | - return check_user_password_V4(user_password, data); | 525 | + if (getV() < 5) { |
| 526 | + return check_user_password_V4(user_password); | ||
| 536 | } else { | 527 | } else { |
| 537 | - return check_user_password_V5(user_password, data); | 528 | + return check_user_password_V5(user_password); |
| 538 | } | 529 | } |
| 539 | } | 530 | } |
| 540 | 531 | ||
| 541 | -static bool | ||
| 542 | -check_owner_password_V4( | ||
| 543 | - std::string& user_password, std::string const& owner_password, QPDF::EncryptionData const& data) | 532 | +bool |
| 533 | +QPDF::EncryptionData::check_owner_password_V4( | ||
| 534 | + std::string& user_password, std::string const& owner_password) const | ||
| 544 | { | 535 | { |
| 545 | // Algorithm 3.7 from the PDF 1.7 Reference Manual | 536 | // Algorithm 3.7 from the PDF 1.7 Reference Manual |
| 546 | 537 | ||
| 547 | unsigned char key[OU_key_bytes_V4]; | 538 | unsigned char key[OU_key_bytes_V4]; |
| 548 | - compute_O_rc4_key(user_password, owner_password, data, key); | 539 | + compute_O_rc4_key(user_password, owner_password, key); |
| 549 | unsigned char O_data[key_bytes]; | 540 | unsigned char O_data[key_bytes]; |
| 550 | - memcpy(O_data, QUtil::unsigned_char_pointer(data.getO()), key_bytes); | 541 | + memcpy(O_data, QUtil::unsigned_char_pointer(getO()), key_bytes); |
| 551 | std::string k1(reinterpret_cast<char*>(key), OU_key_bytes_V4); | 542 | std::string k1(reinterpret_cast<char*>(key), OU_key_bytes_V4); |
| 552 | - pad_short_parameter(k1, QIntC::to_size(data.getLengthBytes())); | 543 | + pad_short_parameter(k1, QIntC::to_size(getLengthBytes())); |
| 553 | iterate_rc4( | 544 | iterate_rc4( |
| 554 | O_data, | 545 | O_data, |
| 555 | key_bytes, | 546 | key_bytes, |
| 556 | QUtil::unsigned_char_pointer(k1), | 547 | QUtil::unsigned_char_pointer(k1), |
| 557 | - data.getLengthBytes(), | ||
| 558 | - (data.getR() >= 3) ? 20 : 1, | 548 | + getLengthBytes(), |
| 549 | + (getR() >= 3) ? 20 : 1, | ||
| 559 | true); | 550 | true); |
| 560 | std::string new_user_password = std::string(reinterpret_cast<char*>(O_data), key_bytes); | 551 | std::string new_user_password = std::string(reinterpret_cast<char*>(O_data), key_bytes); |
| 561 | - bool result = false; | ||
| 562 | - if (check_user_password(new_user_password, data)) { | ||
| 563 | - result = true; | 552 | + if (check_user_password(new_user_password)) { |
| 564 | user_password = new_user_password; | 553 | user_password = new_user_password; |
| 554 | + return true; | ||
| 565 | } | 555 | } |
| 566 | - return result; | 556 | + return false; |
| 567 | } | 557 | } |
| 568 | 558 | ||
| 569 | -static bool | ||
| 570 | -check_owner_password_V5(std::string const& owner_password, QPDF::EncryptionData const& data) | 559 | +bool |
| 560 | +QPDF::EncryptionData::check_owner_password_V5(std::string const& owner_password) const | ||
| 571 | { | 561 | { |
| 572 | // Algorithm 3.12 from the PDF 1.7 extension level 3 | 562 | // Algorithm 3.12 from the PDF 1.7 extension level 3 |
| 573 | 563 | ||
| 574 | - std::string user_data = data.getU().substr(0, 48); | ||
| 575 | - std::string owner_data = data.getO().substr(0, 32); | ||
| 576 | - std::string validation_salt = data.getO().substr(32, 8); | 564 | + std::string user_data = getU().substr(0, 48); |
| 565 | + std::string owner_data = getO().substr(0, 32); | ||
| 566 | + std::string validation_salt = getO().substr(32, 8); | ||
| 577 | std::string password = truncate_password_V5(owner_password); | 567 | std::string password = truncate_password_V5(owner_password); |
| 578 | - return (hash_V5(password, validation_salt, user_data, data) == owner_data); | 568 | + return hash_V5(password, validation_salt, user_data) == owner_data; |
| 579 | } | 569 | } |
| 580 | 570 | ||
| 581 | -static bool | ||
| 582 | -check_owner_password( | ||
| 583 | - std::string& user_password, std::string const& owner_password, QPDF::EncryptionData const& data) | 571 | +bool |
| 572 | +QPDF::EncryptionData::check_owner_password( | ||
| 573 | + std::string& user_password, std::string const& owner_password) const | ||
| 584 | { | 574 | { |
| 585 | - if (data.getV() < 5) { | ||
| 586 | - return check_owner_password_V4(user_password, owner_password, data); | 575 | + if (getV() < 5) { |
| 576 | + return check_owner_password_V4(user_password, owner_password); | ||
| 587 | } else { | 577 | } else { |
| 588 | - return check_owner_password_V5(owner_password, data); | 578 | + return check_owner_password_V5(owner_password); |
| 589 | } | 579 | } |
| 590 | } | 580 | } |
| 591 | 581 | ||
| 592 | std::string | 582 | std::string |
| 593 | -QPDF::recover_encryption_key_with_password(std::string const& password, EncryptionData const& data) | 583 | +QPDF::EncryptionData::recover_encryption_key_with_password(std::string const& password) const |
| 594 | { | 584 | { |
| 595 | // Disregard whether Perms is valid. | 585 | // Disregard whether Perms is valid. |
| 596 | bool disregard; | 586 | bool disregard; |
| 597 | - return recover_encryption_key_with_password(password, data, disregard); | 587 | + return recover_encryption_key_with_password(password, disregard); |
| 598 | } | 588 | } |
| 599 | 589 | ||
| 600 | -static void | ||
| 601 | -compute_U_UE_value_V5( | 590 | +void |
| 591 | +QPDF::EncryptionData::compute_U_UE_value_V5( | ||
| 602 | std::string const& user_password, | 592 | std::string const& user_password, |
| 603 | std::string const& encryption_key, | 593 | std::string const& encryption_key, |
| 604 | - QPDF::EncryptionData const& data, | ||
| 605 | - std::string& U, | ||
| 606 | - std::string& UE) | 594 | + std::string& out_U, |
| 595 | + std::string& out_UE) const | ||
| 607 | { | 596 | { |
| 608 | // Algorithm 3.8 from the PDF 1.7 extension level 3 | 597 | // Algorithm 3.8 from the PDF 1.7 extension level 3 |
| 609 | char k[16]; | 598 | char k[16]; |
| 610 | QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k)); | 599 | QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k)); |
| 611 | std::string validation_salt(k, 8); | 600 | std::string validation_salt(k, 8); |
| 612 | std::string key_salt(k + 8, 8); | 601 | std::string key_salt(k + 8, 8); |
| 613 | - U = hash_V5(user_password, validation_salt, "", data) + validation_salt + key_salt; | ||
| 614 | - std::string intermediate_key = hash_V5(user_password, key_salt, "", data); | ||
| 615 | - UE = process_with_aes(intermediate_key, true, encryption_key); | 602 | + out_U = hash_V5(user_password, validation_salt, "") + validation_salt + key_salt; |
| 603 | + std::string intermediate_key = hash_V5(user_password, key_salt, ""); | ||
| 604 | + out_UE = process_with_aes(intermediate_key, true, encryption_key); | ||
| 616 | } | 605 | } |
| 617 | 606 | ||
| 618 | -static void | ||
| 619 | -compute_O_OE_value_V5( | 607 | +void |
| 608 | +QPDF::EncryptionData::compute_O_OE_value_V5( | ||
| 620 | std::string const& owner_password, | 609 | std::string const& owner_password, |
| 621 | std::string const& encryption_key, | 610 | std::string const& encryption_key, |
| 622 | - QPDF::EncryptionData const& data, | ||
| 623 | - std::string const& U, | ||
| 624 | - std::string& O, | ||
| 625 | - std::string& OE) | 611 | + std::string const& in_U, |
| 612 | + std::string& out_O, | ||
| 613 | + std::string& out_OE) const | ||
| 626 | { | 614 | { |
| 627 | // Algorithm 3.9 from the PDF 1.7 extension level 3 | 615 | // Algorithm 3.9 from the PDF 1.7 extension level 3 |
| 628 | char k[16]; | 616 | char k[16]; |
| 629 | QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k)); | 617 | QUtil::initializeWithRandomBytes(reinterpret_cast<unsigned char*>(k), sizeof(k)); |
| 630 | std::string validation_salt(k, 8); | 618 | std::string validation_salt(k, 8); |
| 631 | std::string key_salt(k + 8, 8); | 619 | std::string key_salt(k + 8, 8); |
| 632 | - O = hash_V5(owner_password, validation_salt, U, data) + validation_salt + key_salt; | ||
| 633 | - std::string intermediate_key = hash_V5(owner_password, key_salt, U, data); | ||
| 634 | - OE = process_with_aes(intermediate_key, true, encryption_key); | 620 | + out_O = hash_V5(owner_password, validation_salt, in_U) + validation_salt + key_salt; |
| 621 | + std::string intermediate_key = hash_V5(owner_password, key_salt, in_U); | ||
| 622 | + out_OE = process_with_aes(intermediate_key, true, encryption_key); | ||
| 635 | } | 623 | } |
| 636 | 624 | ||
| 637 | void | 625 | void |
| 638 | -compute_Perms_value_V5_clear( | ||
| 639 | - std::string const& encryption_key, QPDF::EncryptionData const& data, unsigned char k[16]) | 626 | +QPDF::EncryptionData::compute_Perms_value_V5_clear( |
| 627 | + std::string const& encryption_key, unsigned char k[16]) const | ||
| 640 | { | 628 | { |
| 641 | // From algorithm 3.10 from the PDF 1.7 extension level 3 | 629 | // From algorithm 3.10 from the PDF 1.7 extension level 3 |
| 642 | unsigned long long extended_perms = | 630 | unsigned long long extended_perms = |
| 643 | - 0xffffffff00000000LL | static_cast<unsigned long long>(data.getP()); | 631 | + 0xffffffff00000000LL | static_cast<unsigned long long>(getP()); |
| 644 | for (int i = 0; i < 8; ++i) { | 632 | for (int i = 0; i < 8; ++i) { |
| 645 | k[i] = static_cast<unsigned char>(extended_perms & 0xff); | 633 | k[i] = static_cast<unsigned char>(extended_perms & 0xff); |
| 646 | extended_perms >>= 8; | 634 | extended_perms >>= 8; |
| 647 | } | 635 | } |
| 648 | - k[8] = data.getEncryptMetadata() ? 'T' : 'F'; | 636 | + k[8] = getEncryptMetadata() ? 'T' : 'F'; |
| 649 | k[9] = 'a'; | 637 | k[9] = 'a'; |
| 650 | k[10] = 'd'; | 638 | k[10] = 'd'; |
| 651 | k[11] = 'b'; | 639 | k[11] = 'b'; |
| 652 | QUtil::initializeWithRandomBytes(k + 12, 4); | 640 | QUtil::initializeWithRandomBytes(k + 12, 4); |
| 653 | } | 641 | } |
| 654 | 642 | ||
| 655 | -static std::string | ||
| 656 | -compute_Perms_value_V5(std::string const& encryption_key, QPDF::EncryptionData const& data) | 643 | +std::string |
| 644 | +QPDF::EncryptionData::compute_Perms_value_V5(std::string const& encryption_key) const | ||
| 657 | { | 645 | { |
| 658 | // Algorithm 3.10 from the PDF 1.7 extension level 3 | 646 | // Algorithm 3.10 from the PDF 1.7 extension level 3 |
| 659 | unsigned char k[16]; | 647 | unsigned char k[16]; |
| 660 | - compute_Perms_value_V5_clear(encryption_key, data, k); | 648 | + compute_Perms_value_V5_clear(encryption_key, k); |
| 661 | return process_with_aes( | 649 | return process_with_aes( |
| 662 | encryption_key, true, std::string(reinterpret_cast<char*>(k), sizeof(k))); | 650 | encryption_key, true, std::string(reinterpret_cast<char*>(k), sizeof(k))); |
| 663 | } | 651 | } |
| 664 | 652 | ||
| 665 | std::string | 653 | std::string |
| 666 | -QPDF::recover_encryption_key_with_password( | ||
| 667 | - std::string const& password, EncryptionData const& data, bool& perms_valid) | 654 | +QPDF::EncryptionData::recover_encryption_key_with_password( |
| 655 | + std::string const& password, bool& perms_valid) const | ||
| 668 | { | 656 | { |
| 669 | // Algorithm 3.2a from the PDF 1.7 extension level 3 | 657 | // Algorithm 3.2a from the PDF 1.7 extension level 3 |
| 670 | 658 | ||
| @@ -677,21 +665,21 @@ QPDF::recover_encryption_key_with_password( | @@ -677,21 +665,21 @@ QPDF::recover_encryption_key_with_password( | ||
| 677 | std::string key_salt; | 665 | std::string key_salt; |
| 678 | std::string user_data; | 666 | std::string user_data; |
| 679 | std::string encrypted_file_key; | 667 | std::string encrypted_file_key; |
| 680 | - if (check_owner_password_V5(key_password, data)) { | ||
| 681 | - key_salt = data.getO().substr(40, 8); | ||
| 682 | - user_data = data.getU().substr(0, 48); | ||
| 683 | - encrypted_file_key = data.getOE().substr(0, 32); | ||
| 684 | - } else if (check_user_password_V5(key_password, data)) { | ||
| 685 | - key_salt = data.getU().substr(40, 8); | ||
| 686 | - encrypted_file_key = data.getUE().substr(0, 32); | 668 | + if (check_owner_password_V5(key_password)) { |
| 669 | + key_salt = getO().substr(40, 8); | ||
| 670 | + user_data = getU().substr(0, 48); | ||
| 671 | + encrypted_file_key = getOE().substr(0, 32); | ||
| 672 | + } else if (check_user_password_V5(key_password)) { | ||
| 673 | + key_salt = getU().substr(40, 8); | ||
| 674 | + encrypted_file_key = getUE().substr(0, 32); | ||
| 687 | } | 675 | } |
| 688 | - std::string intermediate_key = hash_V5(key_password, key_salt, user_data, data); | 676 | + std::string intermediate_key = hash_V5(key_password, key_salt, user_data); |
| 689 | std::string file_key = process_with_aes(intermediate_key, false, encrypted_file_key); | 677 | std::string file_key = process_with_aes(intermediate_key, false, encrypted_file_key); |
| 690 | 678 | ||
| 691 | // Decrypt Perms and check against expected value | 679 | // Decrypt Perms and check against expected value |
| 692 | - std::string perms_check = process_with_aes(file_key, false, data.getPerms(), 12); | 680 | + auto perms_check = process_with_aes(file_key, false, getPerms()).substr(0, 12); |
| 693 | unsigned char k[16]; | 681 | unsigned char k[16]; |
| 694 | - compute_Perms_value_V5_clear(file_key, data, k); | 682 | + compute_Perms_value_V5_clear(file_key, k); |
| 695 | perms_valid = (memcmp(perms_check.c_str(), k, 12) == 0); | 683 | perms_valid = (memcmp(perms_check.c_str(), k, 12) == 0); |
| 696 | 684 | ||
| 697 | return file_key; | 685 | return file_key; |
| @@ -909,7 +897,7 @@ QPDF::initializeEncryption() | @@ -909,7 +897,7 @@ QPDF::initializeEncryption() | ||
| 909 | // ignore passwords in file | 897 | // ignore passwords in file |
| 910 | } else { | 898 | } else { |
| 911 | m->encp->owner_password_matched = | 899 | m->encp->owner_password_matched = |
| 912 | - check_owner_password(m->encp->user_password, m->encp->provided_password, data); | 900 | + data.check_owner_password(m->encp->user_password, m->encp->provided_password); |
| 913 | if (m->encp->owner_password_matched && (V < 5)) { | 901 | if (m->encp->owner_password_matched && (V < 5)) { |
| 914 | // password supplied was owner password; user_password has been initialized for V < 5 | 902 | // password supplied was owner password; user_password has been initialized for V < 5 |
| 915 | if (getTrimmedUserPassword() == m->encp->provided_password) { | 903 | if (getTrimmedUserPassword() == m->encp->provided_password) { |
| @@ -917,7 +905,7 @@ QPDF::initializeEncryption() | @@ -917,7 +905,7 @@ QPDF::initializeEncryption() | ||
| 917 | QTC::TC("qpdf", "QPDF_encryption user matches owner V < 5"); | 905 | QTC::TC("qpdf", "QPDF_encryption user matches owner V < 5"); |
| 918 | } | 906 | } |
| 919 | } else { | 907 | } else { |
| 920 | - m->encp->user_password_matched = check_user_password(m->encp->provided_password, data); | 908 | + m->encp->user_password_matched = data.check_user_password(m->encp->provided_password); |
| 921 | if (m->encp->user_password_matched) { | 909 | if (m->encp->user_password_matched) { |
| 922 | m->encp->user_password = m->encp->provided_password; | 910 | m->encp->user_password = m->encp->provided_password; |
| 923 | } | 911 | } |
| @@ -941,12 +929,11 @@ QPDF::initializeEncryption() | @@ -941,12 +929,11 @@ QPDF::initializeEncryption() | ||
| 941 | // neither password can be used to recover the other. | 929 | // neither password can be used to recover the other. |
| 942 | bool perms_valid; | 930 | bool perms_valid; |
| 943 | m->encp->encryption_key = | 931 | m->encp->encryption_key = |
| 944 | - recover_encryption_key_with_password(m->encp->provided_password, data, perms_valid); | 932 | + data.recover_encryption_key_with_password(m->encp->provided_password, perms_valid); |
| 945 | if (!perms_valid) { | 933 | if (!perms_valid) { |
| 946 | warn(damagedPDF( | 934 | warn(damagedPDF( |
| 947 | "encryption dictionary", | 935 | "encryption dictionary", |
| 948 | - "/Perms field in encryption dictionary doesn't match expected " | ||
| 949 | - "value")); | 936 | + "/Perms field in encryption dictionary doesn't match expected value")); |
| 950 | } | 937 | } |
| 951 | } | 938 | } |
| 952 | } | 939 | } |
| @@ -1167,14 +1154,20 @@ QPDF::compute_encryption_O_U( | @@ -1167,14 +1154,20 @@ QPDF::compute_encryption_O_U( | ||
| 1167 | std::string& O, | 1154 | std::string& O, |
| 1168 | std::string& U) | 1155 | std::string& U) |
| 1169 | { | 1156 | { |
| 1157 | + EncryptionData(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata) | ||
| 1158 | + .compute_encryption_O_U(user_password, owner_password, O, U); | ||
| 1159 | +} | ||
| 1160 | + | ||
| 1161 | +void | ||
| 1162 | +QPDF::EncryptionData::compute_encryption_O_U( | ||
| 1163 | + char const* user_password, char const* owner_password, std::string& out_O, std::string& out_U) | ||
| 1164 | +{ | ||
| 1170 | if (V >= 5) { | 1165 | if (V >= 5) { |
| 1171 | throw std::logic_error("compute_encryption_O_U called for file with V >= 5"); | 1166 | throw std::logic_error("compute_encryption_O_U called for file with V >= 5"); |
| 1172 | } | 1167 | } |
| 1173 | - EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata); | ||
| 1174 | - data.setO(compute_O_value(user_password, owner_password, data)); | ||
| 1175 | - O = data.getO(); | ||
| 1176 | - data.setU(compute_U_value(user_password, data)); | ||
| 1177 | - U = data.getU(); | 1168 | + setO(compute_O_value(user_password, owner_password)); |
| 1169 | + out_O = getO(); | ||
| 1170 | + out_U = compute_U_value(user_password); | ||
| 1178 | } | 1171 | } |
| 1179 | 1172 | ||
| 1180 | void | 1173 | void |
| @@ -1194,14 +1187,29 @@ QPDF::compute_encryption_parameters_V5( | @@ -1194,14 +1187,29 @@ QPDF::compute_encryption_parameters_V5( | ||
| 1194 | std::string& UE, | 1187 | std::string& UE, |
| 1195 | std::string& Perms) | 1188 | std::string& Perms) |
| 1196 | { | 1189 | { |
| 1197 | - EncryptionData data(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata); | 1190 | + EncryptionData(V, R, key_len, P, "", "", "", "", "", id1, encrypt_metadata) |
| 1191 | + .compute_encryption_parameters_V5( | ||
| 1192 | + user_password, owner_password, encryption_key, O, U, OE, UE, Perms); | ||
| 1193 | +} | ||
| 1194 | + | ||
| 1195 | +void | ||
| 1196 | +QPDF::EncryptionData::compute_encryption_parameters_V5( | ||
| 1197 | + char const* user_password, | ||
| 1198 | + char const* owner_password, | ||
| 1199 | + std::string& out_encryption_key, | ||
| 1200 | + std::string& out_O, | ||
| 1201 | + std::string& out_U, | ||
| 1202 | + std::string& out_OE, | ||
| 1203 | + std::string& out_UE, | ||
| 1204 | + std::string& out_Perms) | ||
| 1205 | +{ | ||
| 1198 | unsigned char k[key_bytes]; | 1206 | unsigned char k[key_bytes]; |
| 1199 | QUtil::initializeWithRandomBytes(k, key_bytes); | 1207 | QUtil::initializeWithRandomBytes(k, key_bytes); |
| 1200 | - encryption_key = std::string(reinterpret_cast<char*>(k), key_bytes); | ||
| 1201 | - compute_U_UE_value_V5(user_password, encryption_key, data, U, UE); | ||
| 1202 | - compute_O_OE_value_V5(owner_password, encryption_key, data, U, O, OE); | ||
| 1203 | - Perms = compute_Perms_value_V5(encryption_key, data); | ||
| 1204 | - data.setV5EncryptionParameters(O, OE, U, UE, Perms); | 1208 | + out_encryption_key = std::string(reinterpret_cast<char*>(k), key_bytes); |
| 1209 | + compute_U_UE_value_V5(user_password, out_encryption_key, out_U, out_UE); | ||
| 1210 | + compute_O_OE_value_V5(owner_password, out_encryption_key, out_U, out_O, out_OE); | ||
| 1211 | + out_Perms = compute_Perms_value_V5(out_encryption_key); | ||
| 1212 | + setV5EncryptionParameters(out_O, out_OE, out_U, out_UE, out_Perms); | ||
| 1205 | } | 1213 | } |
| 1206 | 1214 | ||
| 1207 | std::string const& | 1215 | std::string const& |