mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 10:58:58 +00:00
First increment of improving handling of weak crypto (fixes #358)
This commit is contained in:
parent
37916f3925
commit
750aca5b94
@ -1,3 +1,9 @@
|
|||||||
|
2021-11-10 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* Add --allow-weak-crypto option to suppress warnings about use of
|
||||||
|
weak cryptographic algorithms. Update documentation around this
|
||||||
|
issue. Fixes #358.
|
||||||
|
|
||||||
2021-11-07 Jay Berkenbilt <ejb@ql.org>
|
2021-11-07 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
* Relax xref recovery logic a bit so that files whose objects are
|
* Relax xref recovery logic a bit so that files whose objects are
|
||||||
|
@ -62,6 +62,10 @@ For example, if you want to guarantee that the GnuTLS crypto provider is used, y
|
|||||||
|
|
||||||
Please see the section on crypto providers in the manual for more details.
|
Please see the section on crypto providers in the manual for more details.
|
||||||
|
|
||||||
|
## Note about weak cryptographic algorithms
|
||||||
|
|
||||||
|
The PDF file format used to rely on RC4 for encryption. Using 256-bit keys always uses AES instead, and with 128-bit keys, you can elect to use AES. qpdf does its best to warn when someone is writing a file with weak cryptographic algorithms, but qpdf must always retain support for being able to read and even write files with weak encryption to be able to fully support older PDF files and older PDF readers.
|
||||||
|
|
||||||
# Building from a pristine checkout
|
# Building from a pristine checkout
|
||||||
|
|
||||||
When building qpdf from a pristine checkout from version control, generated documentation files are not present. You may either generate them (by passing `--enable-doc-maintenance` to `./configure` and satisfying the extra build-time dependencies) or obtain them from a released source package, which includes them. If you want to grab just the files that are in the source distribution but not in the repository, extract a source distribution in a temporary directory, and run `make CLEAN=1 distfiles.zip`. This will create a file called `distfiles.zip`, which can you can extract in a checkout of the source repository. This step is optional unless you are running make install and want the html and PDF versions of the documentation to be installed.
|
When building qpdf from a pristine checkout from version control, generated documentation files are not present. You may either generate them (by passing `--enable-doc-maintenance` to `./configure` and satisfying the extra build-time dependencies) or obtain them from a released source package, which includes them. If you want to grab just the files that are in the source distribution but not in the repository, extract a source distribution in a temporary directory, and run `make CLEAN=1 distfiles.zip`. This will create a file called `distfiles.zip`, which can you can extract in a checkout of the source repository. This step is optional unless you are running make install and want the html and PDF versions of the documentation to be installed.
|
||||||
|
17
TODO
17
TODO
@ -187,6 +187,23 @@ Comments appear in the code prefixed by "ABI"
|
|||||||
before copying, though maybe we don't because it could cause
|
before copying, though maybe we don't because it could cause
|
||||||
multiple copies to be made...usually it's better to handle that
|
multiple copies to be made...usually it's better to handle that
|
||||||
explicitly.
|
explicitly.
|
||||||
|
* Deal with weak cryptographic algorithms:
|
||||||
|
* Github issue #576
|
||||||
|
* Add something to QPDFWriter that you must call in order to allow
|
||||||
|
creation of files with insecure crypto. Maybe
|
||||||
|
QPDFWriter::allowWeakCrypto. Call this when --allow-weak-crypto is
|
||||||
|
passed and probably also when copying encryption by default from
|
||||||
|
an input file.
|
||||||
|
* Change deterministic id to use something other than MD5 but allow
|
||||||
|
the old way for compatibility -- maybe rename the method to force
|
||||||
|
the developer to make a choice
|
||||||
|
* Find other uses of MD5 and find the ones that are discretionary,
|
||||||
|
if any
|
||||||
|
* Have QPDFWriter raise an exception if it's about to write using
|
||||||
|
weak crypto and hasn't been given permission
|
||||||
|
* Search for --allow-weak-crypto in the manual and in qpdf.cc's help
|
||||||
|
information
|
||||||
|
* Update the ref.weak-crypto section of the manual
|
||||||
|
|
||||||
Page splitting/merging
|
Page splitting/merging
|
||||||
======================
|
======================
|
||||||
|
@ -107,7 +107,7 @@ FuzzHelper::testWrite()
|
|||||||
w->setStaticID(true);
|
w->setStaticID(true);
|
||||||
w->setObjectStreamMode(qpdf_o_disable);
|
w->setObjectStreamMode(qpdf_o_disable);
|
||||||
w->setR3EncryptionParameters(
|
w->setR3EncryptionParameters(
|
||||||
"u", "o", true, true, qpdf_r3p_full, qpdf_r3m_all);
|
"u", "o", true, true, true, true, true, true, qpdf_r3p_full);
|
||||||
doWrite(w);
|
doWrite(w);
|
||||||
|
|
||||||
q = getQpdf();
|
q = getQpdf();
|
||||||
|
@ -69,6 +69,9 @@ class QPDF_DLL_CLASS QPDFCryptoImpl
|
|||||||
|
|
||||||
// Encryption/Decryption
|
// Encryption/Decryption
|
||||||
|
|
||||||
|
// QPDF must support RC4 to be able to work with older PDF files
|
||||||
|
// and readers. Search for RC4 in README.md
|
||||||
|
|
||||||
// key_len of -1 means treat key_data as a null-terminated string
|
// key_len of -1 means treat key_data as a null-terminated string
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
virtual void RC4_init(unsigned char const* key_data, int key_len = -1) = 0;
|
virtual void RC4_init(unsigned char const* key_data, int key_len = -1) = 0;
|
||||||
|
@ -359,6 +359,16 @@ class QPDFWriter
|
|||||||
// this from your own application, QUtil contains many transcoding
|
// this from your own application, QUtil contains many transcoding
|
||||||
// functions that could be useful to you, most notably
|
// functions that could be useful to you, most notably
|
||||||
// utf8_to_pdf_doc.
|
// utf8_to_pdf_doc.
|
||||||
|
|
||||||
|
// R3 uses RC4, which is a weak cryptographic algorithm. Don't use
|
||||||
|
// it unless you have to.
|
||||||
|
QPDF_DLL
|
||||||
|
void setR2EncryptionParameters(
|
||||||
|
char const* user_password, char const* owner_password,
|
||||||
|
bool allow_print, bool allow_modify,
|
||||||
|
bool allow_extract, bool allow_annotate);
|
||||||
|
// R3 uses RC4, which is a weak cryptographic algorithm. Don't use
|
||||||
|
// it unless you have to.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR3EncryptionParameters(
|
void setR3EncryptionParameters(
|
||||||
char const* user_password, char const* owner_password,
|
char const* user_password, char const* owner_password,
|
||||||
@ -366,6 +376,8 @@ class QPDFWriter
|
|||||||
bool allow_assemble, bool allow_annotate_and_form,
|
bool allow_assemble, bool allow_annotate_and_form,
|
||||||
bool allow_form_filling, bool allow_modify_other,
|
bool allow_form_filling, bool allow_modify_other,
|
||||||
qpdf_r3_print_e print);
|
qpdf_r3_print_e print);
|
||||||
|
// R4 uses RC4, which is a weak cryptographic algorithm, when
|
||||||
|
// use_aes=false. Don't use it unless you have to.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR4EncryptionParameters(
|
void setR4EncryptionParameters(
|
||||||
char const* user_password, char const* owner_password,
|
char const* user_password, char const* owner_password,
|
||||||
@ -392,28 +404,27 @@ class QPDFWriter
|
|||||||
qpdf_r3_print_e print, bool encrypt_metadata_aes);
|
qpdf_r3_print_e print, bool encrypt_metadata_aes);
|
||||||
|
|
||||||
// Pre qpdf 8.4.0 API
|
// Pre qpdf 8.4.0 API
|
||||||
QPDF_DLL
|
[[deprecated("see newer API above")]]
|
||||||
void setR2EncryptionParameters(
|
|
||||||
char const* user_password, char const* owner_password,
|
|
||||||
bool allow_print, bool allow_modify,
|
|
||||||
bool allow_extract, bool allow_annotate);
|
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR3EncryptionParameters(
|
void setR3EncryptionParameters(
|
||||||
char const* user_password, char const* owner_password,
|
char const* user_password, char const* owner_password,
|
||||||
bool allow_accessibility, bool allow_extract,
|
bool allow_accessibility, bool allow_extract,
|
||||||
qpdf_r3_print_e print, qpdf_r3_modify_e modify);
|
qpdf_r3_print_e print, qpdf_r3_modify_e modify);
|
||||||
|
[[deprecated("see newer API above")]]
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR4EncryptionParameters(
|
void setR4EncryptionParameters(
|
||||||
char const* user_password, char const* owner_password,
|
char const* user_password, char const* owner_password,
|
||||||
bool allow_accessibility, bool allow_extract,
|
bool allow_accessibility, bool allow_extract,
|
||||||
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
||||||
bool encrypt_metadata, bool use_aes);
|
bool encrypt_metadata, bool use_aes);
|
||||||
|
[[deprecated("see newer API above")]]
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR5EncryptionParameters(
|
void setR5EncryptionParameters(
|
||||||
char const* user_password, char const* owner_password,
|
char const* user_password, char const* owner_password,
|
||||||
bool allow_accessibility, bool allow_extract,
|
bool allow_accessibility, bool allow_extract,
|
||||||
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
||||||
bool encrypt_metadata);
|
bool encrypt_metadata);
|
||||||
|
[[deprecated("see newer API above")]]
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR6EncryptionParameters(
|
void setR6EncryptionParameters(
|
||||||
char const* user_password, char const* owner_password,
|
char const* user_password, char const* owner_password,
|
||||||
|
11
lgtm.yml
Normal file
11
lgtm.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# See https://lgtm.com/help/lgtm/lgtm.yml-configuration-file
|
||||||
|
|
||||||
|
# Suppress alerts for weak cryptographic algorithms. The PDF file
|
||||||
|
# format used to rely on various weak algorithms. It is no longer
|
||||||
|
# necessary to use them when creating encrypted PDF files, but qpdf
|
||||||
|
# must always retain support for those algorithms in order to be able
|
||||||
|
# to read older files. qpdf issues warnings to try to prevent users
|
||||||
|
# from creating new files with weak encryption algorithms.
|
||||||
|
|
||||||
|
queries:
|
||||||
|
- exclude: cpp/weak-cryptographic-algorithm
|
@ -34,6 +34,7 @@ Pl_RC4::write(unsigned char* data, size_t len)
|
|||||||
size_t bytes =
|
size_t bytes =
|
||||||
(bytes_left < this->out_bufsize ? bytes_left : out_bufsize);
|
(bytes_left < this->out_bufsize ? bytes_left : out_bufsize);
|
||||||
bytes_left -= bytes;
|
bytes_left -= bytes;
|
||||||
|
// lgtm[cpp/weak-cryptographic-algorithm]
|
||||||
rc4.process(p, bytes, outbuf.getPointer());
|
rc4.process(p, bytes, outbuf.getPointer());
|
||||||
p += bytes;
|
p += bytes;
|
||||||
getNext()->write(outbuf.getPointer(), bytes);
|
getNext()->write(outbuf.getPointer(), bytes);
|
||||||
|
@ -702,10 +702,20 @@ void qpdf_set_r3_encryption_parameters(
|
|||||||
QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract,
|
QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract,
|
||||||
qpdf_r3_print_e print, qpdf_r3_modify_e modify)
|
qpdf_r3_print_e print, qpdf_r3_modify_e modify)
|
||||||
{
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable: 4996)
|
||||||
|
#endif
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
#endif
|
||||||
qpdf->qpdf_writer->setR3EncryptionParameters(
|
qpdf->qpdf_writer->setR3EncryptionParameters(
|
||||||
user_password, owner_password,
|
user_password, owner_password,
|
||||||
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
||||||
print, modify);
|
print, modify);
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void qpdf_set_r4_encryption_parameters(
|
void qpdf_set_r4_encryption_parameters(
|
||||||
@ -714,11 +724,21 @@ void qpdf_set_r4_encryption_parameters(
|
|||||||
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
||||||
QPDF_BOOL encrypt_metadata, QPDF_BOOL use_aes)
|
QPDF_BOOL encrypt_metadata, QPDF_BOOL use_aes)
|
||||||
{
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable: 4996)
|
||||||
|
#endif
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
#endif
|
||||||
qpdf->qpdf_writer->setR4EncryptionParameters(
|
qpdf->qpdf_writer->setR4EncryptionParameters(
|
||||||
user_password, owner_password,
|
user_password, owner_password,
|
||||||
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
||||||
print, modify,
|
print, modify,
|
||||||
encrypt_metadata != QPDF_FALSE, use_aes != QPDF_FALSE);
|
encrypt_metadata != QPDF_FALSE, use_aes != QPDF_FALSE);
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void qpdf_set_r5_encryption_parameters(
|
void qpdf_set_r5_encryption_parameters(
|
||||||
@ -727,11 +747,21 @@ void qpdf_set_r5_encryption_parameters(
|
|||||||
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
||||||
QPDF_BOOL encrypt_metadata)
|
QPDF_BOOL encrypt_metadata)
|
||||||
{
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable: 4996)
|
||||||
|
#endif
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
#endif
|
||||||
qpdf->qpdf_writer->setR5EncryptionParameters(
|
qpdf->qpdf_writer->setR5EncryptionParameters(
|
||||||
user_password, owner_password,
|
user_password, owner_password,
|
||||||
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
||||||
print, modify,
|
print, modify,
|
||||||
encrypt_metadata != QPDF_FALSE);
|
encrypt_metadata != QPDF_FALSE);
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void qpdf_set_r6_encryption_parameters(
|
void qpdf_set_r6_encryption_parameters(
|
||||||
@ -740,10 +770,20 @@ void qpdf_set_r6_encryption_parameters(
|
|||||||
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
qpdf_r3_print_e print, qpdf_r3_modify_e modify,
|
||||||
QPDF_BOOL encrypt_metadata)
|
QPDF_BOOL encrypt_metadata)
|
||||||
{
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable: 4996)
|
||||||
|
#endif
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
#endif
|
||||||
qpdf->qpdf_writer->setR6EncryptionParameters(
|
qpdf->qpdf_writer->setR6EncryptionParameters(
|
||||||
user_password, owner_password,
|
user_password, owner_password,
|
||||||
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
allow_accessibility != QPDF_FALSE, allow_extract != QPDF_FALSE,
|
||||||
print, modify, encrypt_metadata != QPDF_FALSE);
|
print, modify, encrypt_metadata != QPDF_FALSE);
|
||||||
|
#if (defined(__GNUC__) || defined(__clang__))
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value)
|
void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value)
|
||||||
|
@ -873,6 +873,19 @@ make
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--allow-weak-crypto</option></term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Starting with version 10.4, qpdf issues warnings when
|
||||||
|
requested to create files using RC4 encryption. This option
|
||||||
|
suppresses those warnings. In future versions of qpdf, qpdf
|
||||||
|
will refuse to create files with weak cryptography when this
|
||||||
|
flag is not given. See <xref linkend="ref.weak-crypto"/> for
|
||||||
|
additional details.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><option>--encrypt options --</option></term>
|
<term><option>--encrypt options --</option></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -3355,6 +3368,43 @@ outfile.pdf</option>
|
|||||||
</para>
|
</para>
|
||||||
</sect1>
|
</sect1>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
<chapter id="ref.weak-crypto">
|
||||||
|
<title>Weak Cryptography</title>
|
||||||
|
<para>
|
||||||
|
Start with version 10.4, qpdf is taking steps to reduce the
|
||||||
|
likelihood of a user <emphasis>accidentally</emphasis> creating PDF
|
||||||
|
files with insecure cryptography but will continue to allow
|
||||||
|
creation of such files indefinitely with explicit acknowledgment.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
The PDF file format makes use of RC4, which is known to be a weak
|
||||||
|
cryptography algorithm, and MD5, which is a weak hashing algorithm.
|
||||||
|
In version 10.4, qpdf generates warnings for some (but not all)
|
||||||
|
cases of writing files with weak cryptography when invoked from the
|
||||||
|
command-line. These warnings can be suppressed using the
|
||||||
|
<option>--allow-weak-crypto</option> option.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
It is planned for qpdf version 11 to be stricter, making it an
|
||||||
|
error to write files with insecure cryptography from the
|
||||||
|
command-line tool in most cases without specifying the
|
||||||
|
<option>--allow-weak-crypto</option> flag and also to require
|
||||||
|
explicit steps when using the C++ library to enable use of insecure
|
||||||
|
cryptography.
|
||||||
|
</para>
|
||||||
|
<para>
|
||||||
|
Note that qpdf must always retain support for weak cryptographic
|
||||||
|
algorithms since this is required for reading older PDF files that
|
||||||
|
use it. Additionally, qpdf will always retain the ability to create
|
||||||
|
files using weak cryptographic algorithms since, as a development
|
||||||
|
tool, qpdf explicitly supports creating older or deprecated types
|
||||||
|
of PDF files since these are sometimes needed to test or work with
|
||||||
|
older versions of software. Even if other cryptography libraries
|
||||||
|
drop support for RC4 or MD5, qpdf can always fall back to its
|
||||||
|
internal implementations of those algorithms, so they are not going
|
||||||
|
to disappear from qpdf.
|
||||||
|
</para>
|
||||||
|
</chapter>
|
||||||
<chapter id="ref.json">
|
<chapter id="ref.json">
|
||||||
<title>QPDF JSON</title>
|
<title>QPDF JSON</title>
|
||||||
<sect1 id="ref.json-overview">
|
<sect1 id="ref.json-overview">
|
||||||
@ -5070,6 +5120,27 @@ print "\n";
|
|||||||
<term>10.4.0: Month dd, YYYY</term>
|
<term>10.4.0: Month dd, YYYY</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
Handling of Weak Cryptography Algorithms
|
||||||
|
</para>
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
From the qpdf CLI, the <option>--allow-weak-crypto</option>
|
||||||
|
is now required to suppress a warning when explicitly
|
||||||
|
creating PDF files using RC4 encryption. While qpdf will
|
||||||
|
always retain the ability to read and write such files,
|
||||||
|
doing so will require explicit acknowledgment moving
|
||||||
|
forward. For qpdf 10.4, this change only affects the
|
||||||
|
command-line tool. Starting in qpdf 11, there will be small
|
||||||
|
API changes to require explicit acknowledgment in those
|
||||||
|
cases as well. For additional information, see <xref
|
||||||
|
linkend="ref.weak-crypto"/>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</listitem>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
|
37
qpdf/qpdf.cc
37
qpdf/qpdf.cc
@ -141,6 +141,7 @@ struct Options
|
|||||||
suppress_password_recovery(false),
|
suppress_password_recovery(false),
|
||||||
password_mode(pm_auto),
|
password_mode(pm_auto),
|
||||||
allow_insecure(false),
|
allow_insecure(false),
|
||||||
|
allow_weak_crypto(false),
|
||||||
keylen(0),
|
keylen(0),
|
||||||
r2_print(true),
|
r2_print(true),
|
||||||
r2_modify(true),
|
r2_modify(true),
|
||||||
@ -242,6 +243,7 @@ struct Options
|
|||||||
bool suppress_password_recovery;
|
bool suppress_password_recovery;
|
||||||
password_mode_e password_mode;
|
password_mode_e password_mode;
|
||||||
bool allow_insecure;
|
bool allow_insecure;
|
||||||
|
bool allow_weak_crypto;
|
||||||
std::string user_password;
|
std::string user_password;
|
||||||
std::string owner_password;
|
std::string owner_password;
|
||||||
int keylen;
|
int keylen;
|
||||||
@ -811,6 +813,7 @@ class ArgParser
|
|||||||
void argDecrypt();
|
void argDecrypt();
|
||||||
void argPasswordIsHexKey();
|
void argPasswordIsHexKey();
|
||||||
void argAllowInsecure();
|
void argAllowInsecure();
|
||||||
|
void argAllowWeakCrypto();
|
||||||
void argPasswordMode(char* parameter);
|
void argPasswordMode(char* parameter);
|
||||||
void argSuppressPasswordRecovery();
|
void argSuppressPasswordRecovery();
|
||||||
void argCopyEncryption(char* parameter);
|
void argCopyEncryption(char* parameter);
|
||||||
@ -1169,6 +1172,7 @@ ArgParser::initOptionTable()
|
|||||||
(*t)["replace-input"] = oe_bare(&ArgParser::argReplaceInput);
|
(*t)["replace-input"] = oe_bare(&ArgParser::argReplaceInput);
|
||||||
(*t)["is-encrypted"] = oe_bare(&ArgParser::argIsEncrypted);
|
(*t)["is-encrypted"] = oe_bare(&ArgParser::argIsEncrypted);
|
||||||
(*t)["requires-password"] = oe_bare(&ArgParser::argRequiresPassword);
|
(*t)["requires-password"] = oe_bare(&ArgParser::argRequiresPassword);
|
||||||
|
(*t)["allow-weak-crypto"] = oe_bare(&ArgParser::argAllowWeakCrypto);
|
||||||
|
|
||||||
t = &this->encrypt40_option_table;
|
t = &this->encrypt40_option_table;
|
||||||
(*t)["--"] = oe_bare(&ArgParser::argEndEncrypt);
|
(*t)["--"] = oe_bare(&ArgParser::argEndEncrypt);
|
||||||
@ -1376,6 +1380,8 @@ ArgParser::argHelp()
|
|||||||
<< "--encryption-file-password=password\n"
|
<< "--encryption-file-password=password\n"
|
||||||
<< " password used to open the file from which encryption\n"
|
<< " password used to open the file from which encryption\n"
|
||||||
<< " parameters are being copied\n"
|
<< " parameters are being copied\n"
|
||||||
|
<< "--allow-weak-crypto allow creation of files using weak cryptographic\n"
|
||||||
|
<< " algorithms\n"
|
||||||
<< "--encrypt options -- generate an encrypted file\n"
|
<< "--encrypt options -- generate an encrypted file\n"
|
||||||
<< "--decrypt remove any encryption on the file\n"
|
<< "--decrypt remove any encryption on the file\n"
|
||||||
<< "--password-is-hex-key treat primary password option as a hex-encoded key\n"
|
<< "--password-is-hex-key treat primary password option as a hex-encoded key\n"
|
||||||
@ -1502,6 +1508,11 @@ ArgParser::argHelp()
|
|||||||
<< "to be used even if not otherwise needed. This option is primarily useful\n"
|
<< "to be used even if not otherwise needed. This option is primarily useful\n"
|
||||||
<< "for testing qpdf and has no other practical use.\n"
|
<< "for testing qpdf and has no other practical use.\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
|
<< "A warning will be issued if you attempt to encrypt a file with a format that\n"
|
||||||
|
<< "uses a weak cryptographic algorithm such as RC4. To suppress the warning,\n"
|
||||||
|
<< "specify the option --allow-weak-crypto. This option is outside of encryption\n"
|
||||||
|
<< "options (e.g. --allow-week-crypto --encrypt u o 128 --)\n"
|
||||||
|
<< "\n"
|
||||||
<< "\n"
|
<< "\n"
|
||||||
<< "Password Modes\n"
|
<< "Password Modes\n"
|
||||||
<< "--------------\n"
|
<< "--------------\n"
|
||||||
@ -2068,6 +2079,12 @@ ArgParser::argAllowInsecure()
|
|||||||
o.allow_insecure = true;
|
o.allow_insecure = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ArgParser::argAllowWeakCrypto()
|
||||||
|
{
|
||||||
|
o.allow_weak_crypto = true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ArgParser::argCopyEncryption(char* parameter)
|
ArgParser::argCopyEncryption(char* parameter)
|
||||||
{
|
{
|
||||||
@ -6253,6 +6270,26 @@ static void set_encryption_options(QPDF& pdf, Options& o, QPDFWriter& w)
|
|||||||
}
|
}
|
||||||
maybe_fix_write_password(R, o, o.user_password);
|
maybe_fix_write_password(R, o, o.user_password);
|
||||||
maybe_fix_write_password(R, o, o.owner_password);
|
maybe_fix_write_password(R, o, o.owner_password);
|
||||||
|
if ((R < 4) || ((R == 4) && (! o.use_aes)))
|
||||||
|
{
|
||||||
|
if (! o.allow_weak_crypto)
|
||||||
|
{
|
||||||
|
// Do not set exit code to EXIT_WARNING for this case as
|
||||||
|
// this does not reflect a potential problem with the
|
||||||
|
// input file.
|
||||||
|
QTC::TC("qpdf", "qpdf weak crypto warning");
|
||||||
|
std::cerr
|
||||||
|
<< whoami
|
||||||
|
<< ": writing a file with RC4, a weak cryptographic algorithm"
|
||||||
|
<< std::endl
|
||||||
|
<< "Please use 256-bit keys for better security."
|
||||||
|
<< std::endl
|
||||||
|
<< "Pass --allow-weak-crypto to suppress this warning."
|
||||||
|
<< std::endl
|
||||||
|
<< "This will become an error in a future version of qpdf."
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
switch (R)
|
switch (R)
|
||||||
{
|
{
|
||||||
case 2:
|
case 2:
|
||||||
|
@ -598,3 +598,4 @@ check unclosed --pages 1
|
|||||||
QPDF_pages findPage not found 0
|
QPDF_pages findPage not found 0
|
||||||
qpdf overlay page with no resources 0
|
qpdf overlay page with no resources 0
|
||||||
QPDFObjectHandle check ownership 0
|
QPDFObjectHandle check ownership 0
|
||||||
|
qpdf weak crypto warning 0
|
||||||
|
@ -1358,7 +1358,8 @@ foreach my $file (qw(short-id long-id))
|
|||||||
{
|
{
|
||||||
$td->runtest("encrypt $file.pdf",
|
$td->runtest("encrypt $file.pdf",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --encrypt '' pass 40 -- $file.pdf a.pdf"},
|
"qpdf --allow-weak-crypto".
|
||||||
|
" --encrypt '' pass 40 -- $file.pdf a.pdf"},
|
||||||
{$td->STRING => "",
|
{$td->STRING => "",
|
||||||
$td->EXIT_STATUS => 0},
|
$td->EXIT_STATUS => 0},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
@ -2057,7 +2058,8 @@ $td->notify("--- Split Pages ---");
|
|||||||
my @sp_cases = (
|
my @sp_cases = (
|
||||||
[11, '%d at beginning', '', '%d_split-out.zdf'],
|
[11, '%d at beginning', '', '%d_split-out.zdf'],
|
||||||
[11, '%d at end', '--qdf', 'split-out.zdf_%d'],
|
[11, '%d at end', '--qdf', 'split-out.zdf_%d'],
|
||||||
[11, '%d in middle', '--encrypt u o 128 --', 'a-%d-split-out.zdf'],
|
[11, '%d in middle', '--allow-weak-crypto --encrypt u o 128 --',
|
||||||
|
'a-%d-split-out.zdf'],
|
||||||
[11, 'pdf extension', '', 'split-out.Pdf'],
|
[11, 'pdf extension', '', 'split-out.Pdf'],
|
||||||
[4, 'fallback', '--pages 11-pages.pdf 1-3 minimal.pdf --', 'split-out'],
|
[4, 'fallback', '--pages 11-pages.pdf 1-3 minimal.pdf --', 'split-out'],
|
||||||
[1, 'broken data', '--pages broken-lzw.pdf --', 'split-out.pdf',
|
[1, 'broken data', '--pages broken-lzw.pdf --', 'split-out.pdf',
|
||||||
@ -2718,6 +2720,7 @@ $td->runtest("check output",
|
|||||||
$td->runtest("avoid respecification of password",
|
$td->runtest("avoid respecification of password",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --empty a.pdf --copy-encryption=20-pages.pdf" .
|
"qpdf --empty a.pdf --copy-encryption=20-pages.pdf" .
|
||||||
|
" --allow-weak-crypto" .
|
||||||
" --encryption-file-password=user" .
|
" --encryption-file-password=user" .
|
||||||
" --pages 20-pages.pdf 1,z -- --static-id"},
|
" --pages 20-pages.pdf 1,z -- --static-id"},
|
||||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
@ -3483,7 +3486,7 @@ for (my $n = 16; $n <= 19; ++$n)
|
|||||||
'-object-streams=preserve',
|
'-object-streams=preserve',
|
||||||
'-object-streams=generate')
|
'-object-streams=generate')
|
||||||
{
|
{
|
||||||
foreach my $qdf ('-qdf', '', '-encrypt "" x 128 --')
|
foreach my $qdf ('-qdf', '', '-allow-weak-crypto -encrypt "" x 128 --')
|
||||||
{
|
{
|
||||||
# 4 tests + 1 compare_pdfs * 36 cases
|
# 4 tests + 1 compare_pdfs * 36 cases
|
||||||
# 2 additional tests * 12 cases
|
# 2 additional tests * 12 cases
|
||||||
@ -3716,19 +3719,22 @@ foreach my $f (qw(compressed-metadata.pdf enc-base.pdf))
|
|||||||
check_metadata("a.pdf", 0, 1);
|
check_metadata("a.pdf", 0, 1);
|
||||||
$td->runtest("encrypt normally",
|
$td->runtest("encrypt normally",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --encrypt '' o 128 -- a.pdf b.pdf"},
|
"qpdf --allow-weak-crypto" .
|
||||||
|
" --encrypt '' o 128 -- a.pdf b.pdf"},
|
||||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
check_metadata("b.pdf", 1, 0);
|
check_metadata("b.pdf", 1, 0);
|
||||||
unlink "b.pdf";
|
unlink "b.pdf";
|
||||||
$td->runtest("encrypt V4",
|
$td->runtest("encrypt V4",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --encrypt '' o 128 --force-V4 -- a.pdf b.pdf"},
|
"qpdf --allow-weak-crypto" .
|
||||||
|
" --encrypt '' o 128 --force-V4 -- a.pdf b.pdf"},
|
||||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
check_metadata("b.pdf", 1, 0);
|
check_metadata("b.pdf", 1, 0);
|
||||||
unlink "b.pdf";
|
unlink "b.pdf";
|
||||||
$td->runtest("encrypt with cleartext metadata",
|
$td->runtest("encrypt with cleartext metadata",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --encrypt '' o 128 --cleartext-metadata --" .
|
"qpdf --allow-weak-crypto" .
|
||||||
|
" --encrypt '' o 128 --cleartext-metadata --" .
|
||||||
" a.pdf b.pdf"},
|
" a.pdf b.pdf"},
|
||||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
check_metadata("b.pdf", 1, 1);
|
check_metadata("b.pdf", 1, 1);
|
||||||
@ -3751,6 +3757,31 @@ foreach my $f (qw(compressed-metadata.pdf enc-base.pdf))
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show_ntests();
|
||||||
|
# ----------
|
||||||
|
$td->notify("--- Weak Cryptography ---");
|
||||||
|
$n_tests += 4;
|
||||||
|
$td->runtest("256-bit: no warning",
|
||||||
|
{$td->COMMAND => 'qpdf --encrypt "" "" 256 -- minimal.pdf a.pdf'},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("128-bit with AES: no warning",
|
||||||
|
{$td->COMMAND => 'qpdf --encrypt "" "" 128 --use-aes=y --' .
|
||||||
|
' minimal.pdf a.pdf'},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
# Note: we intentionally have exit status 0 for this warning.
|
||||||
|
$td->runtest("128-bit without AES: warning",
|
||||||
|
{$td->COMMAND => 'qpdf --encrypt "" "" 128 -- minimal.pdf a.pdf'},
|
||||||
|
{$td->REGEXP => "Pass --allow-weak-crypto to suppress",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("40-bit: warning",
|
||||||
|
{$td->COMMAND => 'qpdf --encrypt "" "" 40 -- minimal.pdf a.pdf'},
|
||||||
|
{$td->REGEXP => "Pass --allow-weak-crypto to suppress",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Linearization Tests ---");
|
$td->notify("--- Linearization Tests ---");
|
||||||
@ -4128,7 +4159,8 @@ foreach my $d (@encrypted_files)
|
|||||||
$enc_json =~ s/---opm---/$opm/;
|
$enc_json =~ s/---opm---/$opm/;
|
||||||
$enc_json =~ s/---upm---/$upm/;
|
$enc_json =~ s/---upm---/$upm/;
|
||||||
|
|
||||||
my $eflags = "-encrypt \"$upass\" \"$opass\" $bits $xeflags --";
|
my $eflags = "--allow-weak-crypto" .
|
||||||
|
" -encrypt \"$upass\" \"$opass\" $bits $xeflags --";
|
||||||
if (($opass eq "") && ($bits == 256))
|
if (($opass eq "") && ($bits == 256))
|
||||||
{
|
{
|
||||||
$eflags =~ s/--$/--allow-insecure --/;
|
$eflags =~ s/--$/--allow-insecure --/;
|
||||||
@ -4391,7 +4423,7 @@ foreach my $d (['--force-V4', 'V4'],
|
|||||||
my ($args, $out) = @$d;
|
my ($args, $out) = @$d;
|
||||||
$td->runtest("encrypt $args",
|
$td->runtest("encrypt $args",
|
||||||
{$td->COMMAND => "qpdf --static-aes-iv --static-id" .
|
{$td->COMMAND => "qpdf --static-aes-iv --static-id" .
|
||||||
" --encrypt '' '' 128 $args --" .
|
" --allow-weak-crypto --encrypt '' '' 128 $args --" .
|
||||||
" enc-base.pdf a.pdf"},
|
" enc-base.pdf a.pdf"},
|
||||||
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
$td->runtest("check output",
|
$td->runtest("check output",
|
||||||
@ -4677,6 +4709,7 @@ foreach my $d (@unicode_pw_cases)
|
|||||||
$td->runtest("encode $bits, $pw, $w_encoding",
|
$td->runtest("encode $bits, $pw, $w_encoding",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf $xargs --static-id --static-aes-iv" .
|
"qpdf $xargs --static-id --static-aes-iv" .
|
||||||
|
" --allow-weak-crypto" .
|
||||||
" --encrypt $upass o $bits -- minimal.pdf a.pdf"},
|
" --encrypt $upass o $bits -- minimal.pdf a.pdf"},
|
||||||
{$td->STRING => $exp, $td->EXIT_STATUS => ($exp ? 2 : 0)},
|
{$td->STRING => $exp, $td->EXIT_STATUS => ($exp ? 2 : 0)},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
@ -4718,7 +4751,8 @@ $n_tests += 5;
|
|||||||
|
|
||||||
$td->runtest("bytes fallback warning",
|
$td->runtest("bytes fallback warning",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --encrypt \@password-bare-complex-utf8 o 128 --" .
|
"qpdf --allow-weak-crypto" .
|
||||||
|
" --encrypt \@password-bare-complex-utf8 o 128 --" .
|
||||||
" minimal.pdf a.pdf"},
|
" minimal.pdf a.pdf"},
|
||||||
{$td->FILE => "bytes-fallback.out", $td->EXIT_STATUS => 0},
|
{$td->FILE => "bytes-fallback.out", $td->EXIT_STATUS => 0},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
@ -4814,9 +4848,9 @@ my @flags = (["-qdf", # 1
|
|||||||
"decrypted"],
|
"decrypted"],
|
||||||
["-linearize", # 9
|
["-linearize", # 9
|
||||||
"linearized"],
|
"linearized"],
|
||||||
["-encrypt \"\" owner 128 --", # 10
|
["-allow-weak-crypto -encrypt \"\" owner 128 --", # 10
|
||||||
"encrypted"],
|
"encrypted"],
|
||||||
["-linearize -encrypt \"\" o 128 --", # 11
|
["-linearize -allow-weak-crypto -encrypt \"\" o 128 --", # 11
|
||||||
"linearized and encrypted"],
|
"linearized and encrypted"],
|
||||||
["", # 12
|
["", # 12
|
||||||
"no arguments"],
|
"no arguments"],
|
||||||
@ -4985,9 +5019,15 @@ $n_tests += 2;
|
|||||||
$n_tests += 12;
|
$n_tests += 12;
|
||||||
foreach my $i (qw(40 128 256))
|
foreach my $i (qw(40 128 256))
|
||||||
{
|
{
|
||||||
|
my $x = "";
|
||||||
|
if ($i < 256)
|
||||||
|
{
|
||||||
|
$x = "--allow-weak-crypto";
|
||||||
|
}
|
||||||
$td->runtest("encrypt $i",
|
$td->runtest("encrypt $i",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --encrypt '' o $i -- digitally-signed.pdf a.pdf"},
|
"qpdf $x --encrypt '' o $i --" .
|
||||||
|
" digitally-signed.pdf a.pdf"},
|
||||||
{$td->STRING => "",
|
{$td->STRING => "",
|
||||||
$td->EXIT_STATUS => 0});
|
$td->EXIT_STATUS => 0});
|
||||||
$td->runtest("find desired contents (encrypt $i)",
|
$td->runtest("find desired contents (encrypt $i)",
|
||||||
@ -5010,9 +5050,15 @@ foreach my $i (qw(40 128 256))
|
|||||||
$n_tests += 15;
|
$n_tests += 15;
|
||||||
foreach my $i (qw(40 128 256))
|
foreach my $i (qw(40 128 256))
|
||||||
{
|
{
|
||||||
|
my $x = "";
|
||||||
|
if ($i < 256)
|
||||||
|
{
|
||||||
|
$x = "--allow-weak-crypto";
|
||||||
|
}
|
||||||
$td->runtest("non sig dict encrypt $i",
|
$td->runtest("non sig dict encrypt $i",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --encrypt '' o $i -- comment-annotation.pdf a.pdf"},
|
"qpdf $x --encrypt '' o $i --" .
|
||||||
|
" comment-annotation.pdf a.pdf"},
|
||||||
{$td->STRING => "",
|
{$td->STRING => "",
|
||||||
$td->EXIT_STATUS => 0});
|
$td->EXIT_STATUS => 0});
|
||||||
$td->runtest("plain text not found due to encryption (non sig dict encrypt $i)",
|
$td->runtest("plain text not found due to encryption (non sig dict encrypt $i)",
|
||||||
|
Loading…
Reference in New Issue
Block a user