aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
authorJay Berkenbilt <ejb@ql.org>2023-12-23 00:14:11 +0100
committerJay Berkenbilt <ejb@ql.org>2023-12-23 02:10:18 +0100
commit7d7e2234a537b6cd2205fb5cf942d5a9e8a866e3 (patch)
treefc4553803a5510c0e2793cc57d055dd5459e2ff6 /libqpdf
parent1173a0bdfc56a08eedafc06afcd37f0b35ac3ea2 (diff)
downloadqpdf-7d7e2234a537b6cd2205fb5cf942d5a9e8a866e3.tar.zst
Implement new --encrypt args and completion (fixes #784)
Positional arguments are supported in a backward-compatible way, but completion no longer guides users to it.
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/QPDFJob_argv.cc74
-rw-r--r--libqpdf/qpdf/auto_job_help.hh73
2 files changed, 86 insertions, 61 deletions
diff --git a/libqpdf/QPDFJob_argv.cc b/libqpdf/QPDFJob_argv.cc
index adf7ba64..56acd7a9 100644
--- a/libqpdf/QPDFJob_argv.cc
+++ b/libqpdf/QPDFJob_argv.cc
@@ -35,6 +35,9 @@ namespace
std::shared_ptr<QPDFJob::EncConfig> c_enc;
std::vector<std::string> accumulated_args;
std::shared_ptr<char> pages_password{nullptr};
+ std::string user_password;
+ std::string owner_password;
+ bool used_enc_password_args{false};
bool gave_input{false};
bool gave_output{false};
};
@@ -161,64 +164,67 @@ void
ArgParser::argEncrypt()
{
this->accumulated_args.clear();
- if (this->ap.isCompleting() && this->ap.argsLeft() == 0) {
- this->ap.insertCompletion("user-password");
- }
this->ap.selectOptionTable(O_ENCRYPTION);
}
void
ArgParser::argEncPositional(std::string const& arg)
{
+ if (used_enc_password_args) {
+ usage("positional and dashed encryption arguments may not be mixed");
+ }
+
this->accumulated_args.push_back(arg);
- size_t n_args = this->accumulated_args.size();
- if (n_args < 3) {
- if (this->ap.isCompleting() && (this->ap.argsLeft() == 0)) {
- if (n_args == 1) {
- this->ap.insertCompletion("owner-password");
- } else if (n_args == 2) {
- this->ap.insertCompletion("40");
- this->ap.insertCompletion("128");
- this->ap.insertCompletion("256");
- }
- }
+ if (this->accumulated_args.size() < 3) {
return;
}
- std::string user_password = this->accumulated_args.at(0);
- std::string owner_password = this->accumulated_args.at(1);
- std::string len_str = this->accumulated_args.at(2);
- int keylen = 0;
- if (len_str == "40") {
- keylen = 40;
- this->ap.selectOptionTable(O_40_BIT_ENCRYPTION);
- } else if (len_str == "128") {
- keylen = 128;
- this->ap.selectOptionTable(O_128_BIT_ENCRYPTION);
- } else if (len_str == "256") {
- keylen = 256;
- this->ap.selectOptionTable(O_256_BIT_ENCRYPTION);
- } else {
- usage("encryption key length must be 40, 128, or 256");
- }
- this->c_enc = c_main->encrypt(keylen, user_password, owner_password);
+ user_password = this->accumulated_args.at(0);
+ owner_password = this->accumulated_args.at(1);
+ auto len_str = this->accumulated_args.at(2);
+ this->accumulated_args.clear();
+ argEncBits(len_str);
}
void
ArgParser::argEncUserPassword(std::string const& arg)
{
- // QXXXQ
+ if (!accumulated_args.empty()) {
+ usage("positional and dashed encryption arguments may not be mixed");
+ }
+ this->used_enc_password_args = true;
+ this->user_password = arg;
}
void
ArgParser::argEncOwnerPassword(std::string const& arg)
{
- // QXXXQ
+ if (!accumulated_args.empty()) {
+ usage("positional and dashed encryption arguments may not be mixed");
+ }
+ this->used_enc_password_args = true;
+ this->owner_password = arg;
}
void
ArgParser::argEncBits(std::string const& arg)
{
- // QXXXQ
+ if (!accumulated_args.empty()) {
+ usage("positional and dashed encryption arguments may not be mixed");
+ }
+ int keylen = 0;
+ if (arg == "40") {
+ keylen = 40;
+ this->ap.selectOptionTable(O_40_BIT_ENCRYPTION);
+ } else if (arg == "128") {
+ keylen = 128;
+ this->ap.selectOptionTable(O_128_BIT_ENCRYPTION);
+ } else if (arg == "256") {
+ keylen = 256;
+ this->ap.selectOptionTable(O_256_BIT_ENCRYPTION);
+ } else {
+ usage("encryption key length must be 40, 128, or 256");
+ }
+ this->c_enc = c_main->encrypt(keylen, user_password, owner_password);
}
void
diff --git a/libqpdf/qpdf/auto_job_help.hh b/libqpdf/qpdf/auto_job_help.hh
index 9f7cadfe..b96c2564 100644
--- a/libqpdf/qpdf/auto_job_help.hh
+++ b/libqpdf/qpdf/auto_job_help.hh
@@ -148,29 +148,14 @@ the structure without changing the content.
)");
ap.addOptionHelp("--linearize", "transformation", "linearize (web-optimize) output", R"(Create linearized (web-optimized) output files.
)");
-ap.addOptionHelp("--encrypt", "transformation", "start encryption options", R"(--encrypt user-password owner-password key-length [options] --
+ap.addOptionHelp("--encrypt", "transformation", "start encryption options", R"(--encrypt [options] --
Run qpdf --help=encryption for details.
)");
-ap.addOptionHelp("--user-password", "transformation", "specify user password", R"(--user-password=user-password
-
-Set the user password.
-)");
-ap.addOptionHelp("--owner-password", "transformation", "specify owner password", R"(--owner-password=owner-password
-
-Set the owner password.
-)");
-ap.addOptionHelp("--bits", "transformation", "specify encryption bit depth", R"(--bits={48|128|256}
-
-Set the encrypt bit depth. Use 256.
-)");
ap.addOptionHelp("--decrypt", "transformation", "remove encryption from input file", R"(Create an unencrypted output file even if the input file was
encrypted. Normally qpdf preserves whatever encryption was
present on the input file. This option overrides that behavior.
)");
-}
-static void add_help_3(QPDFArgParser& ap)
-{
ap.addOptionHelp("--remove-restrictions", "transformation", "remove security restrictions from input file", R"(Remove restrictions associated with digitally signed PDF files.
This may be combined with --decrypt to allow free editing of
previously signed/encrypted files. This option invalidates the
@@ -187,6 +172,9 @@ ap.addOptionHelp("--encryption-file-password", "transformation", "supply passwor
If the file named in --copy-encryption requires a password, use
this option to supply the password.
)");
+}
+static void add_help_3(QPDFArgParser& ap)
+{
ap.addOptionHelp("--qdf", "transformation", "enable viewing PDF code in a text editor", R"(Create a PDF file suitable for viewing in a text editor and even
editing. This is for editing the PDF code, not the page contents.
All streams that can be uncompressed are uncompressed, and
@@ -282,9 +270,6 @@ ap.addOptionHelp("--ii-min-bytes", "transformation", "set minimum size for --ext
Don't externalize inline images smaller than this size. The
default is 1,024. Use 0 for no minimum.
)");
-}
-static void add_help_4(QPDFArgParser& ap)
-{
ap.addOptionHelp("--min-version", "transformation", "set minimum PDF version", R"(--min-version=version
Force the PDF version of the output to be at least the specified
@@ -312,6 +297,9 @@ resulting set of pages, where :odd starts with the first page and
:even starts with the second page. These are odd and even pages
from the resulting set, not based on the original page numbers.
)");
+}
+static void add_help_4(QPDFArgParser& ap)
+{
ap.addHelpTopic("modification", "change parts of the PDF", R"(Modification options make systematic changes to certain parts of
the PDF, causing the PDF to render differently from the original.
)");
@@ -404,18 +392,33 @@ ap.addOptionHelp("--keep-inline-images", "modification", "exclude inline images
)");
ap.addOptionHelp("--remove-page-labels", "modification", "remove explicit page numbers", R"(Exclude page labels (explicit page numbers) from the output file.
)");
-}
-static void add_help_5(QPDFArgParser& ap)
-{
ap.addHelpTopic("encryption", "create encrypted files", R"(Create encrypted files. Usage:
+--encrypt \
+ [--user-password=user-password] \
+ [--owner-password=owner-password] \
+ --bits=key-length [options] --
+
+OR
+
--encrypt user-password owner-password key-length [options] --
-Either or both of user-password and owner-password may be empty
-strings, though setting either to the empty string enables the file
-to be opened and decrypted without a password. key-length may be
-40, 128, or 256. Encryption options are terminated by "--" by
-itself.
+The first form, with flags for the passwords and bit length, was
+introduced in qpdf 11.7.0. Only the --bits option is is mandatory.
+This form allows you to use any text as the password. If passwords
+are specified, they must be given before the --bits option.
+
+The second form has been in qpdf since the beginning and wil
+continue to be supported. Either or both of user-password and
+owner-password may be empty strings.
+
+The key-length parameter must be either 40, 128, or 256. The user
+and/or owner password may be omitted. Omitting either pasword
+enables the PDF file to be opened without a password. Specifying
+the same value for the user and owner password and specifying an
+empty owner password are both considered insecure.
+
+Encryption options are terminated by "--" by itself.
40-bit encryption is insecure, as is 128-bit encryption without
AES. Use 256-bit encryption unless you have a specific reason to
@@ -468,6 +471,22 @@ Values for modify-opt:
annotate form + commenting and modifying forms
all allow full document modification
)");
+ap.addOptionHelp("--user-password", "encryption", "specify user password", R"(--user-password=user-password
+
+Set the user password of the encrypted file.
+)");
+ap.addOptionHelp("--owner-password", "encryption", "specify owner password", R"(--owner-password=owner-password
+
+Set the owner password of the encrypted file.
+)");
+}
+static void add_help_5(QPDFArgParser& ap)
+{
+ap.addOptionHelp("--bits", "encryption", "specify encryption key length", R"(--bits={48|128|256}
+
+Specify the encryption key length. For best security, always use
+a key length of 256.
+)");
ap.addOptionHelp("--accessibility", "encryption", "restrict document accessibility", R"(--accessibility=[y|n]
This option is ignored except with very old encryption formats.