Commit b7bbf12e85fa46e7971d84143d1597c992045af1

Authored by Jay Berkenbilt
1 parent f049a77c

In json mode, reveal recovered user password when otherwise unavailable

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
@@ -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&amp; first, QPDF&amp; pdf) @@ -1382,6 +1382,15 @@ QPDFJob::doJSONEncrypt(Pipeline* p, bool&amp; first, QPDF&amp; 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&lt;std::string&gt;* keys) @@ -1669,6 +1678,7 @@ QPDFJob::json_schema(int json_version, std::set&lt;std::string&gt;* 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
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 "streammethod": "AESv2", 28 "streammethod": "AESv2",
29 "stringmethod": "AESv2" 29 "stringmethod": "AESv2"
30 }, 30 },
  31 + "recovereduserpassword": null,
31 "userpasswordmatched": true 32 "userpasswordmatched": true
32 } 33 }
33 } 34 }
qpdf/qtest/qpdf/json-V4-aes-encrypt---show-encryption-key-v2.out
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 "streammethod": "AESv2", 28 "streammethod": "AESv2",
29 "stringmethod": "AESv2" 29 "stringmethod": "AESv2"
30 }, 30 },
  31 + "recovereduserpassword": null,
31 "userpasswordmatched": true 32 "userpasswordmatched": true
32 } 33 }
33 } 34 }
qpdf/qtest/qpdf/json-V4-aes-encrypt-v1.out
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 "streammethod": "AESv2", 28 "streammethod": "AESv2",
29 "stringmethod": "AESv2" 29 "stringmethod": "AESv2"
30 }, 30 },
  31 + "recovereduserpassword": null,
31 "userpasswordmatched": true 32 "userpasswordmatched": true
32 } 33 }
33 } 34 }
qpdf/qtest/qpdf/json-V4-aes-encrypt-v2.out
@@ -28,6 +28,7 @@ @@ -28,6 +28,7 @@
28 "streammethod": "AESv2", 28 "streammethod": "AESv2",
29 "stringmethod": "AESv2" 29 "stringmethod": "AESv2"
30 }, 30 },
  31 + "recovereduserpassword": null,
31 "userpasswordmatched": true 32 "userpasswordmatched": true
32 } 33 }
33 } 34 }
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": [],