Commit b7bbf12e85fa46e7971d84143d1597c992045af1
1 parent
f049a77c
In json mode, reveal recovered user password when otherwise unavailable
Showing
31 changed files
with
51 additions
and
2 deletions
ChangeLog
| 1 | 2022-05-30 Jay Berkenbilt <ejb@ql.org> | 1 | 2022-05-30 Jay Berkenbilt <ejb@ql.org> |
| 2 | 2 | ||
| 3 | + * When showing encryption data in json output, when the user | ||
| 4 | + password was recovered with by the owner password and the | ||
| 5 | + specified password does not match the user password, reveal the | ||
| 6 | + user password. This is not possible with 256-bit keys. | ||
| 7 | + | ||
| 3 | * Include additional information in --list-attachments --verbose | 8 | * Include additional information in --list-attachments --verbose |
| 4 | and in --json --json-key=attachments. | 9 | and in --json --json-key=attachments. |
| 5 | 10 |
TODO
| @@ -70,8 +70,6 @@ Remaining work: | @@ -70,8 +70,6 @@ Remaining work: | ||
| 70 | 70 | ||
| 71 | * --show-xref: add | 71 | * --show-xref: add |
| 72 | 72 | ||
| 73 | - * --encryption: show recovered user password when available | ||
| 74 | - | ||
| 75 | * Consider having --check, --show-encryption, etc., just select the | 73 | * Consider having --check, --show-encryption, etc., just select the |
| 76 | right keys when in json mode. I don't think I want check on by | 74 | right keys when in json mode. I don't think I want check on by |
| 77 | default, so that might be different. | 75 | default, so that might be different. |
libqpdf/QPDFJob.cc
| @@ -1382,6 +1382,15 @@ QPDFJob::doJSONEncrypt(Pipeline* p, bool& first, QPDF& pdf) | @@ -1382,6 +1382,15 @@ QPDFJob::doJSONEncrypt(Pipeline* p, bool& first, QPDF& pdf) | ||
| 1382 | j_encrypt.addDictionaryMember( | 1382 | j_encrypt.addDictionaryMember( |
| 1383 | "ownerpasswordmatched", | 1383 | "ownerpasswordmatched", |
| 1384 | JSON::makeBool(is_encrypted && pdf.ownerPasswordMatched())); | 1384 | JSON::makeBool(is_encrypted && pdf.ownerPasswordMatched())); |
| 1385 | + if (is_encrypted && (V < 5) && pdf.ownerPasswordMatched() && | ||
| 1386 | + (!pdf.userPasswordMatched())) { | ||
| 1387 | + std::string user_password = pdf.getTrimmedUserPassword(); | ||
| 1388 | + j_encrypt.addDictionaryMember( | ||
| 1389 | + "recovereduserpassword", JSON::makeString(user_password)); | ||
| 1390 | + } else { | ||
| 1391 | + j_encrypt.addDictionaryMember( | ||
| 1392 | + "recovereduserpassword", JSON::makeNull()); | ||
| 1393 | + } | ||
| 1385 | JSON j_capabilities = | 1394 | JSON j_capabilities = |
| 1386 | j_encrypt.addDictionaryMember("capabilities", JSON::makeDictionary()); | 1395 | j_encrypt.addDictionaryMember("capabilities", JSON::makeDictionary()); |
| 1387 | j_capabilities.addDictionaryMember( | 1396 | j_capabilities.addDictionaryMember( |
| @@ -1669,6 +1678,7 @@ QPDFJob::json_schema(int json_version, std::set<std::string>* keys) | @@ -1669,6 +1678,7 @@ QPDFJob::json_schema(int json_version, std::set<std::string>* keys) | ||
| 1669 | }, | 1678 | }, |
| 1670 | "encrypted": "whether the document is encrypted", | 1679 | "encrypted": "whether the document is encrypted", |
| 1671 | "ownerpasswordmatched": "whether supplied password matched owner password; always false for non-encrypted files", | 1680 | "ownerpasswordmatched": "whether supplied password matched owner password; always false for non-encrypted files", |
| 1681 | + "recovereduserpassword": "If the owner password was used to recover the user password, reveal user password; otherwise null", | ||
| 1672 | "parameters": { | 1682 | "parameters": { |
| 1673 | "P": "P value from Encrypt dictionary", | 1683 | "P": "P value from Encrypt dictionary", |
| 1674 | "R": "R value from Encrypt dictionary", | 1684 | "R": "R value from Encrypt dictionary", |
manual/release-notes.rst
| @@ -103,6 +103,12 @@ For a detailed list of changes, please see the file | @@ -103,6 +103,12 @@ For a detailed list of changes, please see the file | ||
| 103 | attachments is also included in the ``attachments`` json key | 103 | attachments is also included in the ``attachments`` json key |
| 104 | with ``--json``. | 104 | with ``--json``. |
| 105 | 105 | ||
| 106 | + - For encrypted files, ``qpdf --json`` reveals the user password | ||
| 107 | + when the specified password did not match the user password and | ||
| 108 | + the owner password was used to recover the user password. The | ||
| 109 | + user password is not recoverable from the owner password when | ||
| 110 | + 256-bit keys are in use. | ||
| 111 | + | ||
| 106 | - Library Enhancements | 112 | - Library Enhancements |
| 107 | 113 | ||
| 108 | - New methods ``insertItemAndGet``, ``appendItemAndGet``, | 114 | - New methods ``insertItemAndGet``, ``appendItemAndGet``, |
qpdf/qtest/encryption.test
| @@ -219,6 +219,7 @@ foreach my $d (@encrypted_files) | @@ -219,6 +219,7 @@ foreach my $d (@encrypted_files) | ||
| 219 | " \"streammethod\": \"---method---\",\n" . | 219 | " \"streammethod\": \"---method---\",\n" . |
| 220 | " \"stringmethod\": \"---method---\"\n" . | 220 | " \"stringmethod\": \"---method---\"\n" . |
| 221 | " },\n" . | 221 | " },\n" . |
| 222 | + " \"recovereduserpassword\": ---rup---,\n" . | ||
| 222 | " \"userpasswordmatched\": ---upm---\n" . | 223 | " \"userpasswordmatched\": ---upm---\n" . |
| 223 | " }\n" . | 224 | " }\n" . |
| 224 | "}\n"; | 225 | "}\n"; |
| @@ -267,6 +268,8 @@ foreach my $d (@encrypted_files) | @@ -267,6 +268,8 @@ foreach my $d (@encrypted_files) | ||
| 267 | my $method = $bits == 256 ? "AESv3" : "RC4"; | 268 | my $method = $bits == 256 ? "AESv3" : "RC4"; |
| 268 | my $opm = ($pass eq $opass ? "true" : "false"); | 269 | my $opm = ($pass eq $opass ? "true" : "false"); |
| 269 | my $upm = ($pass eq $upass ? "true" : "false"); | 270 | my $upm = ($pass eq $upass ? "true" : "false"); |
| 271 | + my $rup = (($pass eq $opass) && ($pass ne $upass) && ($V < 5)) | ||
| 272 | + ? "\"$upass\"" : "null"; | ||
| 270 | $enc_json =~ s/---R---/$R/; | 273 | $enc_json =~ s/---R---/$R/; |
| 271 | $enc_json =~ s/---P---/$P/; | 274 | $enc_json =~ s/---P---/$P/; |
| 272 | $enc_json =~ s/---V---/$V/; | 275 | $enc_json =~ s/---V---/$V/; |
| @@ -274,6 +277,7 @@ foreach my $d (@encrypted_files) | @@ -274,6 +277,7 @@ foreach my $d (@encrypted_files) | ||
| 274 | $enc_json =~ s/---method---/$method/g; | 277 | $enc_json =~ s/---method---/$method/g; |
| 275 | $enc_json =~ s/---opm---/$opm/; | 278 | $enc_json =~ s/---opm---/$opm/; |
| 276 | $enc_json =~ s/---upm---/$upm/; | 279 | $enc_json =~ s/---upm---/$upm/; |
| 280 | + $enc_json =~ s/---rup---/$rup/; | ||
| 277 | 281 | ||
| 278 | my $eflags = "--allow-weak-crypto" . | 282 | my $eflags = "--allow-weak-crypto" . |
| 279 | " -encrypt \"$upass\" \"$opass\" $bits $xeflags --"; | 283 | " -encrypt \"$upass\" \"$opass\" $bits $xeflags --"; |
qpdf/qtest/qpdf/json-V4-aes-encrypt---show-encryption-key-v1.out
qpdf/qtest/qpdf/json-V4-aes-encrypt---show-encryption-key-v2.out
qpdf/qtest/qpdf/json-V4-aes-encrypt-v1.out
qpdf/qtest/qpdf/json-V4-aes-encrypt-v2.out
qpdf/qtest/qpdf/json-attachment-fields-v1.out
| @@ -96,6 +96,7 @@ | @@ -96,6 +96,7 @@ | ||
| 96 | "streammethod": "none", | 96 | "streammethod": "none", |
| 97 | "stringmethod": "none" | 97 | "stringmethod": "none" |
| 98 | }, | 98 | }, |
| 99 | + "recovereduserpassword": null, | ||
| 99 | "userpasswordmatched": false | 100 | "userpasswordmatched": false |
| 100 | }, | 101 | }, |
| 101 | "outlines": [], | 102 | "outlines": [], |
qpdf/qtest/qpdf/json-attachment-fields-v2.out
| @@ -96,6 +96,7 @@ | @@ -96,6 +96,7 @@ | ||
| 96 | "streammethod": "none", | 96 | "streammethod": "none", |
| 97 | "stringmethod": "none" | 97 | "stringmethod": "none" |
| 98 | }, | 98 | }, |
| 99 | + "recovereduserpassword": null, | ||
| 99 | "userpasswordmatched": false | 100 | "userpasswordmatched": false |
| 100 | }, | 101 | }, |
| 101 | "outlines": [], | 102 | "outlines": [], |
qpdf/qtest/qpdf/json-field-types---show-encryption-key-v1.out
| @@ -428,6 +428,7 @@ | @@ -428,6 +428,7 @@ | ||
| 428 | "streammethod": "none", | 428 | "streammethod": "none", |
| 429 | "stringmethod": "none" | 429 | "stringmethod": "none" |
| 430 | }, | 430 | }, |
| 431 | + "recovereduserpassword": null, | ||
| 431 | "userpasswordmatched": false | 432 | "userpasswordmatched": false |
| 432 | }, | 433 | }, |
| 433 | "outlines": [], | 434 | "outlines": [], |
qpdf/qtest/qpdf/json-field-types---show-encryption-key-v2.out
| @@ -428,6 +428,7 @@ | @@ -428,6 +428,7 @@ | ||
| 428 | "streammethod": "none", | 428 | "streammethod": "none", |
| 429 | "stringmethod": "none" | 429 | "stringmethod": "none" |
| 430 | }, | 430 | }, |
| 431 | + "recovereduserpassword": null, | ||
| 431 | "userpasswordmatched": false | 432 | "userpasswordmatched": false |
| 432 | }, | 433 | }, |
| 433 | "outlines": [], | 434 | "outlines": [], |
qpdf/qtest/qpdf/json-field-types-v1.out
| @@ -428,6 +428,7 @@ | @@ -428,6 +428,7 @@ | ||
| 428 | "streammethod": "none", | 428 | "streammethod": "none", |
| 429 | "stringmethod": "none" | 429 | "stringmethod": "none" |
| 430 | }, | 430 | }, |
| 431 | + "recovereduserpassword": null, | ||
| 431 | "userpasswordmatched": false | 432 | "userpasswordmatched": false |
| 432 | }, | 433 | }, |
| 433 | "outlines": [], | 434 | "outlines": [], |
qpdf/qtest/qpdf/json-field-types-v2.out
| @@ -428,6 +428,7 @@ | @@ -428,6 +428,7 @@ | ||
| 428 | "streammethod": "none", | 428 | "streammethod": "none", |
| 429 | "stringmethod": "none" | 429 | "stringmethod": "none" |
| 430 | }, | 430 | }, |
| 431 | + "recovereduserpassword": null, | ||
| 431 | "userpasswordmatched": false | 432 | "userpasswordmatched": false |
| 432 | }, | 433 | }, |
| 433 | "outlines": [], | 434 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-all-v1.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-all-v2.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-small-v1.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-small-v2.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-specialized-v1.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-specialized-v2.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-v1.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-image-streams-v2.out
| @@ -271,6 +271,7 @@ | @@ -271,6 +271,7 @@ | ||
| 271 | "streammethod": "none", | 271 | "streammethod": "none", |
| 272 | "stringmethod": "none" | 272 | "stringmethod": "none" |
| 273 | }, | 273 | }, |
| 274 | + "recovereduserpassword": null, | ||
| 274 | "userpasswordmatched": false | 275 | "userpasswordmatched": false |
| 275 | }, | 276 | }, |
| 276 | "outlines": [], | 277 | "outlines": [], |
qpdf/qtest/qpdf/json-outlines-with-actions-v1.out
| @@ -462,6 +462,7 @@ | @@ -462,6 +462,7 @@ | ||
| 462 | "streammethod": "none", | 462 | "streammethod": "none", |
| 463 | "stringmethod": "none" | 463 | "stringmethod": "none" |
| 464 | }, | 464 | }, |
| 465 | + "recovereduserpassword": null, | ||
| 465 | "userpasswordmatched": false | 466 | "userpasswordmatched": false |
| 466 | }, | 467 | }, |
| 467 | "outlines": [ | 468 | "outlines": [ |
qpdf/qtest/qpdf/json-outlines-with-actions-v2.out
| @@ -462,6 +462,7 @@ | @@ -462,6 +462,7 @@ | ||
| 462 | "streammethod": "none", | 462 | "streammethod": "none", |
| 463 | "stringmethod": "none" | 463 | "stringmethod": "none" |
| 464 | }, | 464 | }, |
| 465 | + "recovereduserpassword": null, | ||
| 465 | "userpasswordmatched": false | 466 | "userpasswordmatched": false |
| 466 | }, | 467 | }, |
| 467 | "outlines": [ | 468 | "outlines": [ |
qpdf/qtest/qpdf/json-outlines-with-old-root-dests-v1.out
| @@ -567,6 +567,7 @@ | @@ -567,6 +567,7 @@ | ||
| 567 | "streammethod": "none", | 567 | "streammethod": "none", |
| 568 | "stringmethod": "none" | 568 | "stringmethod": "none" |
| 569 | }, | 569 | }, |
| 570 | + "recovereduserpassword": null, | ||
| 570 | "userpasswordmatched": false | 571 | "userpasswordmatched": false |
| 571 | }, | 572 | }, |
| 572 | "outlines": [ | 573 | "outlines": [ |
qpdf/qtest/qpdf/json-outlines-with-old-root-dests-v2.out
| @@ -567,6 +567,7 @@ | @@ -567,6 +567,7 @@ | ||
| 567 | "streammethod": "none", | 567 | "streammethod": "none", |
| 568 | "stringmethod": "none" | 568 | "stringmethod": "none" |
| 569 | }, | 569 | }, |
| 570 | + "recovereduserpassword": null, | ||
| 570 | "userpasswordmatched": false | 571 | "userpasswordmatched": false |
| 571 | }, | 572 | }, |
| 572 | "outlines": [ | 573 | "outlines": [ |
qpdf/qtest/qpdf/json-page-labels-and-outlines-v1.out
| @@ -637,6 +637,7 @@ | @@ -637,6 +637,7 @@ | ||
| 637 | "streammethod": "none", | 637 | "streammethod": "none", |
| 638 | "stringmethod": "none" | 638 | "stringmethod": "none" |
| 639 | }, | 639 | }, |
| 640 | + "recovereduserpassword": null, | ||
| 640 | "userpasswordmatched": false | 641 | "userpasswordmatched": false |
| 641 | }, | 642 | }, |
| 642 | "outlines": [ | 643 | "outlines": [ |
qpdf/qtest/qpdf/json-page-labels-and-outlines-v2.out
| @@ -637,6 +637,7 @@ | @@ -637,6 +637,7 @@ | ||
| 637 | "streammethod": "none", | 637 | "streammethod": "none", |
| 638 | "stringmethod": "none" | 638 | "stringmethod": "none" |
| 639 | }, | 639 | }, |
| 640 | + "recovereduserpassword": null, | ||
| 640 | "userpasswordmatched": false | 641 | "userpasswordmatched": false |
| 641 | }, | 642 | }, |
| 642 | "outlines": [ | 643 | "outlines": [ |
qpdf/qtest/qpdf/json-page-labels-num-tree-v1.out
| @@ -534,6 +534,7 @@ | @@ -534,6 +534,7 @@ | ||
| 534 | "streammethod": "none", | 534 | "streammethod": "none", |
| 535 | "stringmethod": "none" | 535 | "stringmethod": "none" |
| 536 | }, | 536 | }, |
| 537 | + "recovereduserpassword": null, | ||
| 537 | "userpasswordmatched": false | 538 | "userpasswordmatched": false |
| 538 | }, | 539 | }, |
| 539 | "outlines": [], | 540 | "outlines": [], |
qpdf/qtest/qpdf/json-page-labels-num-tree-v2.out
| @@ -534,6 +534,7 @@ | @@ -534,6 +534,7 @@ | ||
| 534 | "streammethod": "none", | 534 | "streammethod": "none", |
| 535 | "stringmethod": "none" | 535 | "stringmethod": "none" |
| 536 | }, | 536 | }, |
| 537 | + "recovereduserpassword": null, | ||
| 537 | "userpasswordmatched": false | 538 | "userpasswordmatched": false |
| 538 | }, | 539 | }, |
| 539 | "outlines": [], | 540 | "outlines": [], |