From 966429e718c8a0cd458c8efe5aa463cd0052aad0 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Mon, 14 Jan 2019 22:42:36 -0500 Subject: Update CLI and manual for new encryption granularity (fixes #214) --- ChangeLog | 5 +++ manual/qpdf-manual.xml | 62 +++++++++++++++++++++++++++--- qpdf/qpdf.cc | 42 +++++++++++++++++++- qpdf/qtest/qpdf.test | 18 +++++++++ qpdf/qtest/qpdf/completion-encrypt-128.out | 1 - qpdf/qtest/qpdf/completion-encrypt-256.out | 1 - 6 files changed, 120 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1feaffb0..8f1ed679 100644 --- a/ChangeLog +++ b/ChangeLog @@ -16,6 +16,11 @@ 2019-01-14 Jay Berkenbilt + * Add new CLI flags to 128-bit and 256-bit encryption: --assemble, + --annotate, --form, and --modify-other to control encryption + permissions with more granularity than was allowed with the + --modify flag. Fixes #214. + * Add new versions of QPDFWriter::setR{3,4,5,6}EncryptionParameters that allow individual setting of the various permission bits. The old diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml index 034111c2..c3d62814 100644 --- a/manual/qpdf-manual.xml +++ b/manual/qpdf-manual.xml @@ -786,7 +786,12 @@ make Determines whether or not to allow accessibility to visually - impaired. + impaired. The qpdf library disregards this field when AES is + used or when 256-bit encryption is used. You should really + never disable accessibility, but qpdf lets you do it in case + you need to configure a file this way for testing purposes. + The PDF spec says that conforming readers should disregard + this permission and always allow accessibility. @@ -798,6 +803,45 @@ make + + + + + Determines whether document assembly (rotation and reordering + of pages) is allowed. + + + + + + + + Determines whether modifying annotations is allowed. This + includes adding comments and filling in form fields. Also + allows editing of form fields if + is given. + + + + + + + + Determines whether filling form fields is allowed. + + + + + + + + Allow all document editing except those controlled separately + by the , + , and + options. + + + @@ -829,10 +873,10 @@ make - Controls modify access. + Controls modify access. This way of controlling modify access + has less granularity than new options added in qpdf 8.4. may be - one of the following, each of which implies all the options - that follow it: + one of the following: @@ -841,12 +885,14 @@ make - : allow comment authoring and form operations + : allow comment authoring, form + operations, and document assembly : allow form field fill-in and signing + and document assembly @@ -860,6 +906,12 @@ make + Using the option does not allow you + to create certain combinations of permissions such as allowing + form filling but not allowing document assembly. Starting with + qpdf 8.4, you can either just use the other options to control + fields individually, or you can use something like + to fine tune. diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index 23ca3d10..c0e52a1c 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -631,6 +631,10 @@ class ArgParser void arg128Print(char* parameter); void arg128Modify(char* parameter); void arg128ClearTextMetadata(); + void arg128Assemble(char* parameter); + void arg128Annotate(char* parameter); + void arg128Form(char* parameter); + void arg128ModOther(char* parameter); void arg128UseAes(char* parameter); void arg128ForceV4(); void arg256ForceR5(); @@ -857,11 +861,16 @@ ArgParser::initOptionTable() char const* print128_choices[] = {"full", "low", "none", 0}; (*t)["print"] = oe_requiredChoices( &ArgParser::arg128Print, print128_choices); + (*t)["assemble"] = oe_requiredChoices(&ArgParser::arg128Assemble, yn); + (*t)["annotate"] = oe_requiredChoices(&ArgParser::arg128Annotate, yn); + (*t)["form"] = oe_requiredChoices(&ArgParser::arg128Form, yn); + (*t)["modify-other"] = oe_requiredChoices(&ArgParser::arg128ModOther, yn); char const* modify128_choices[] = {"all", "annotate", "form", "assembly", "none", 0}; (*t)["modify"] = oe_requiredChoices( &ArgParser::arg128Modify, modify128_choices); (*t)["cleartext-metadata"] = oe_bare(&ArgParser::arg128ClearTextMetadata); + // The above 128-bit options are also 256-bit options, so copy // what we have so far. Then continue separately with 128 and 256. this->encrypt256_option_table = this->encrypt128_option_table; @@ -1048,7 +1057,11 @@ ArgParser::argHelp() << " --accessibility=[yn] allow accessibility to visually impaired\n" << " --extract=[yn] allow other text/graphic extraction\n" << " --print=print-opt control printing access\n" - << " --modify=modify-opt control modify access\n" + << " --assemble=[yn] allow document assembly\n" + << " --annotate=[yn] allow commenting/filling form fields\n" + << " --form=[yn] allow filling form fields\n" + << " --modify-other=[yn] allow other modifications\n" + << " --modify=modify-opt control modify access (old way)\n" << " --cleartext-metadata prevents encryption of metadata\n" << " --use-aes=[yn] indicates whether to use AES encryption\n" << " --force-V4 forces use of V=4 encryption handler\n" @@ -1072,7 +1085,8 @@ ArgParser::argHelp() << " assembly allow document assembly only\n" << " none allow no modifications\n" << "\n" - << "The default for each permission option is to be fully permissive.\n" + << "The default for each permission option is to be fully permissive. Please\n" + << "refer to the manual for more details on the modify options.\n" << "\n" << "Specifying cleartext-metadata forces the PDF version to at least 1.5.\n" << "Specifying use of AES forces the PDF version to at least 1.6. These\n" @@ -1914,6 +1928,30 @@ ArgParser::arg128ClearTextMetadata() o.cleartext_metadata = true; } +void +ArgParser::arg128Assemble(char* parameter) +{ + o.r3_assemble = (strcmp(parameter, "y") == 0); +} + +void +ArgParser::arg128Annotate(char* parameter) +{ + o.r3_annotate_and_form = (strcmp(parameter, "y") == 0); +} + +void +ArgParser::arg128Form(char* parameter) +{ + o.r3_form_filling = (strcmp(parameter, "y") == 0); +} + +void +ArgParser::arg128ModOther(char* parameter) +{ + o.r3_modify_other = (strcmp(parameter, "y") == 0); +} + void ArgParser::arg128UseAes(char* parameter) { diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index 44aa5421..45f2a6f4 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -2687,6 +2687,24 @@ my @encrypted_files = ['R3,V2,U=view,O=master', 'master', '-accessibility=n -print=low', -2564, 0, 1, 1, 0, 1, 1, 1, 1, 1], + ['R3,V2,U=view,O=master', 'master', + '-modify=all -assemble=n', -1028, + 1, 1, 1, 1, 0, 1, 1, 1, 0], + ['R3,V2,U=view,O=master', 'master', + '-modify=none -form=y', -1068, + 1, 1, 1, 1, 0, 1, 0, 0, 0], + ['R3,V2,U=view,O=master', 'master', + '-modify=annotate -assemble=n', -1036, + 1, 1, 1, 1, 0, 1, 1, 0, 0], + ['R3,V2,U=view,O=master', 'master', + '-form=n', -260, + 1, 1, 1, 1, 1, 0, 1, 1, 0], + ['R3,V2,U=view,O=master', 'master', + '-annotate=n', -36, + 1, 1, 1, 1, 1, 1, 0, 1, 0], + ['R3,V2,U=view,O=master', 'master', + '-modify-other=n', -12, + 1, 1, 1, 1, 1, 1, 1, 0, 0], ['R2,V1', '', '-print=n -modify=n -extract=n -annotate=n', -64, 0, 0, 0, 0, 0, 0, 0, 0, 0], diff --git a/qpdf/qtest/qpdf/completion-encrypt-128.out b/qpdf/qtest/qpdf/completion-encrypt-128.out index 2c51a616..b16bce76 100644 --- a/qpdf/qtest/qpdf/completion-encrypt-128.out +++ b/qpdf/qtest/qpdf/completion-encrypt-128.out @@ -1,3 +1,2 @@ --force-V4 -!--annotate= !--force-R5 diff --git a/qpdf/qtest/qpdf/completion-encrypt-256.out b/qpdf/qtest/qpdf/completion-encrypt-256.out index 7033fbab..6521d2a2 100644 --- a/qpdf/qtest/qpdf/completion-encrypt-256.out +++ b/qpdf/qtest/qpdf/completion-encrypt-256.out @@ -1,3 +1,2 @@ --force-R5 -!--annotate= !--force-V4 -- cgit v1.2.3-54-g00ecf