mirror of
https://github.com/qpdf/qpdf.git
synced 2024-12-22 19:08:59 +00:00
Mark weak encryption with API changes (fixes #576)
This commit is contained in:
parent
2213ed0c3d
commit
8ccd3a8a89
26
ChangeLog
26
ChangeLog
@ -1,8 +1,32 @@
|
|||||||
2022-04-30 Jay Berkenbilt <ejb@ql.org>
|
2022-04-30 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* QPDFWriter: change encryption API calls
|
||||||
|
- Remove deprecated versions of setR*EncryptionParameters
|
||||||
|
methods from before qpdf 8.4.0
|
||||||
|
- Replace setR2EncryptionParameters with
|
||||||
|
setR2EncryptionParametersInsecure
|
||||||
|
- Replace setR3EncryptionParameters with
|
||||||
|
setR3EncryptionParametersInsecure
|
||||||
|
- Replace setR4EncryptionParameters with
|
||||||
|
setR4EncryptionParametersInsecure
|
||||||
|
|
||||||
|
* C API: change encryption API calls to match C++ interface
|
||||||
|
- Remove pre-8.4.0 functions:
|
||||||
|
- qpdf_set_r3_encryption_parameters
|
||||||
|
- qpdf_set_r4_encryption_parameters
|
||||||
|
- qpdf_set_r5_encryption_parameters
|
||||||
|
- qpdf_set_r6_encryption_parameters
|
||||||
|
- Add "_insecure" to insecure encryption triggers:
|
||||||
|
- Replace void qpdf_set_r2_encryption_parameters
|
||||||
|
with qpdf_set_r2_encryption_parameters_insecure
|
||||||
|
- Replace void qpdf_set_r3_encryption_parameters2
|
||||||
|
with qpdf_set_r3_encryption_parameters_insecure
|
||||||
|
- Replace void qpdf_set_r4_encryption_parameters2
|
||||||
|
with qpdf_set_r4_encryption_parameters_insecure
|
||||||
|
|
||||||
* Make attempting to write encrypted files that use RC4 (40-bit or
|
* Make attempting to write encrypted files that use RC4 (40-bit or
|
||||||
128-bit without AES) an error rather than a warning when
|
128-bit without AES) an error rather than a warning when
|
||||||
--allow-weak-crypto is not specified.
|
--allow-weak-crypto is not specified. Fixes #576.
|
||||||
|
|
||||||
2022-04-29 Jay Berkenbilt <ejb@ql.org>
|
2022-04-29 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
12
TODO
12
TODO
@ -475,18 +475,6 @@ Comments appear in the code prefixed by "ABI". Always Search for ABI
|
|||||||
in source and header files to find items not listed here. Also search
|
in source and header files to find items not listed here. Also search
|
||||||
for "[[deprecated" to find deprecated APIs that can be removed.
|
for "[[deprecated" to find deprecated APIs that can be removed.
|
||||||
|
|
||||||
* 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. There should be some API change so that, when
|
|
||||||
people recompile with qpdf 11, their code won't suddenly stop
|
|
||||||
working. Getting this right will take careful consideration of the
|
|
||||||
developer and user experience. We don't want to create a situation
|
|
||||||
where exactly the same code fails to work in 11 but worked on 10.
|
|
||||||
See #576 for latest notes.
|
|
||||||
|
|
||||||
Page splitting/merging
|
Page splitting/merging
|
||||||
======================
|
======================
|
||||||
|
@ -107,7 +107,7 @@ FuzzHelper::testWrite()
|
|||||||
w = getWriter(q);
|
w = getWriter(q);
|
||||||
w->setStaticID(true);
|
w->setStaticID(true);
|
||||||
w->setObjectStreamMode(qpdf_o_disable);
|
w->setObjectStreamMode(qpdf_o_disable);
|
||||||
w->setR3EncryptionParameters(
|
w->setR3EncryptionParametersInsecure(
|
||||||
"u", "o", true, true, true, true, true, true, qpdf_r3p_full);
|
"u", "o", true, true, true, true, true, true, qpdf_r3p_full);
|
||||||
doWrite(w);
|
doWrite(w);
|
||||||
|
|
||||||
|
@ -366,10 +366,12 @@ class QPDFWriter
|
|||||||
// 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
|
// R2 uses RC4, which is a weak cryptographic algorithm. Don't use
|
||||||
// it unless you have to.
|
// it unless you have to. See "Weak Cryptography" in the manual.
|
||||||
|
// This encryption format is deprecated in the PDF 2.0
|
||||||
|
// specification.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR2EncryptionParameters(
|
void setR2EncryptionParametersInsecure(
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
bool allow_print,
|
bool allow_print,
|
||||||
@ -377,9 +379,11 @@ class QPDFWriter
|
|||||||
bool allow_extract,
|
bool allow_extract,
|
||||||
bool allow_annotate);
|
bool allow_annotate);
|
||||||
// R3 uses RC4, which is a weak cryptographic algorithm. Don't use
|
// R3 uses RC4, which is a weak cryptographic algorithm. Don't use
|
||||||
// it unless you have to.
|
// it unless you have to. See "Weak Cryptography" in the manual.
|
||||||
|
// This encryption format is deprecated in the PDF 2.0
|
||||||
|
// specification.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR3EncryptionParameters(
|
void setR3EncryptionParametersInsecure(
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
bool allow_accessibility,
|
bool allow_accessibility,
|
||||||
@ -389,10 +393,13 @@ class QPDFWriter
|
|||||||
bool allow_form_filling,
|
bool allow_form_filling,
|
||||||
bool allow_modify_other,
|
bool allow_modify_other,
|
||||||
qpdf_r3_print_e print);
|
qpdf_r3_print_e print);
|
||||||
// R4 uses RC4, which is a weak cryptographic algorithm, when
|
// When use_aes=false, this call enables R4 with RC4, which is a
|
||||||
// use_aes=false. Don't use it unless you have to.
|
// weak cryptographic algorithm. Even with use_aes=true, the
|
||||||
|
// overall encryption scheme is weak. Don't use it unless you have
|
||||||
|
// to. See "Weak Cryptography" in the manual. This encryption
|
||||||
|
// format is deprecated in the PDF 2.0 specification.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR4EncryptionParameters(
|
void setR4EncryptionParametersInsecure(
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
bool allow_accessibility,
|
bool allow_accessibility,
|
||||||
@ -419,6 +426,8 @@ class QPDFWriter
|
|||||||
bool allow_modify_other,
|
bool allow_modify_other,
|
||||||
qpdf_r3_print_e print,
|
qpdf_r3_print_e print,
|
||||||
bool encrypt_metadata);
|
bool encrypt_metadata);
|
||||||
|
// This is the only password-based encryption format supported by
|
||||||
|
// the PDF specification.
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void setR6EncryptionParameters(
|
void setR6EncryptionParameters(
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
|
@ -458,8 +458,13 @@ extern "C" {
|
|||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void qpdf_set_preserve_encryption(qpdf_data qpdf, QPDF_BOOL value);
|
void qpdf_set_preserve_encryption(qpdf_data qpdf, QPDF_BOOL value);
|
||||||
|
|
||||||
|
/* The *_insecure functions are identical to the old versions but
|
||||||
|
* have been renamed as a an alert to the caller that they are
|
||||||
|
* insecure. See "Weak Cryptographic" in the manual for
|
||||||
|
* details.
|
||||||
|
*/
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void qpdf_set_r2_encryption_parameters(
|
void qpdf_set_r2_encryption_parameters_insecure(
|
||||||
qpdf_data qpdf,
|
qpdf_data qpdf,
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
@ -469,7 +474,7 @@ extern "C" {
|
|||||||
QPDF_BOOL allow_annotate);
|
QPDF_BOOL allow_annotate);
|
||||||
|
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void qpdf_set_r3_encryption_parameters2(
|
void qpdf_set_r3_encryption_parameters_insecure(
|
||||||
qpdf_data qpdf,
|
qpdf_data qpdf,
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
@ -482,7 +487,7 @@ extern "C" {
|
|||||||
enum qpdf_r3_print_e print);
|
enum qpdf_r3_print_e print);
|
||||||
|
|
||||||
QPDF_DLL
|
QPDF_DLL
|
||||||
void qpdf_set_r4_encryption_parameters2(
|
void qpdf_set_r4_encryption_parameters_insecure(
|
||||||
qpdf_data qpdf,
|
qpdf_data qpdf,
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
|
2
job.sums
2
job.sums
@ -14,4 +14,4 @@ libqpdf/qpdf/auto_job_json_decl.hh 06caa46eaf71db8a50c046f91866baa8087745a947431
|
|||||||
libqpdf/qpdf/auto_job_json_init.hh 06d51f11c117011256e175386eee9946441f3c22b49dd91fc591bbc1fa3bbeec
|
libqpdf/qpdf/auto_job_json_init.hh 06d51f11c117011256e175386eee9946441f3c22b49dd91fc591bbc1fa3bbeec
|
||||||
libqpdf/qpdf/auto_job_schema.hh 43273b9edfc48b1f4cccbff1d2b31916a9057c474ef97d2936b2f1f14170885b
|
libqpdf/qpdf/auto_job_schema.hh 43273b9edfc48b1f4cccbff1d2b31916a9057c474ef97d2936b2f1f14170885b
|
||||||
manual/_ext/qpdf.py e9ac9d6c70642a3d29281ee5ad92ae2422dee8be9306fb8a0bc9dba0ed5e28f3
|
manual/_ext/qpdf.py e9ac9d6c70642a3d29281ee5ad92ae2422dee8be9306fb8a0bc9dba0ed5e28f3
|
||||||
manual/cli.rst 6a2d99acedbd207370a8dc2807f6657323c42bccbe51ebdc6bc2d00f6851219c
|
manual/cli.rst 70258db13d89b0476248e9703bf5f50ffe28fce2a179dfeca241582dd28b455c
|
||||||
|
@ -2815,19 +2815,22 @@ QPDFJob::setEncryptionOptions(QPDF& pdf, QPDFWriter& w)
|
|||||||
QTC::TC("qpdf", "QPDFJob weak crypto error");
|
QTC::TC("qpdf", "QPDFJob weak crypto error");
|
||||||
*(this->m->cerr)
|
*(this->m->cerr)
|
||||||
<< this->m->message_prefix
|
<< this->m->message_prefix
|
||||||
<< ": refusing to write a file with RC4, a weak cryptographic algorithm"
|
<< ": refusing to write a file with RC4, a weak cryptographic "
|
||||||
|
"algorithm"
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< "Please use 256-bit keys for better security." << std::endl
|
<< "Please use 256-bit keys for better security." << std::endl
|
||||||
<< "Pass --allow-weak-crypto to enable writing insecure files."
|
<< "Pass --allow-weak-crypto to enable writing insecure files."
|
||||||
<< std::endl
|
<< std::endl
|
||||||
<< "See also https://qpdf.readthedocs.io/en/stable/weak-crypto.html"
|
<< "See also "
|
||||||
|
"https://qpdf.readthedocs.io/en/stable/weak-crypto.html"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
throw std::runtime_error("refusing to write a file with weak crypto");
|
throw std::runtime_error(
|
||||||
|
"refusing to write a file with weak crypto");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch (R) {
|
switch (R) {
|
||||||
case 2:
|
case 2:
|
||||||
w.setR2EncryptionParameters(
|
w.setR2EncryptionParametersInsecure(
|
||||||
m->user_password.c_str(),
|
m->user_password.c_str(),
|
||||||
m->owner_password.c_str(),
|
m->owner_password.c_str(),
|
||||||
m->r2_print,
|
m->r2_print,
|
||||||
@ -2836,7 +2839,7 @@ QPDFJob::setEncryptionOptions(QPDF& pdf, QPDFWriter& w)
|
|||||||
m->r2_annotate);
|
m->r2_annotate);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
w.setR3EncryptionParameters(
|
w.setR3EncryptionParametersInsecure(
|
||||||
m->user_password.c_str(),
|
m->user_password.c_str(),
|
||||||
m->owner_password.c_str(),
|
m->owner_password.c_str(),
|
||||||
m->r3_accessibility,
|
m->r3_accessibility,
|
||||||
@ -2848,7 +2851,7 @@ QPDFJob::setEncryptionOptions(QPDF& pdf, QPDFWriter& w)
|
|||||||
m->r3_print);
|
m->r3_print);
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
w.setR4EncryptionParameters(
|
w.setR4EncryptionParametersInsecure(
|
||||||
m->user_password.c_str(),
|
m->user_password.c_str(),
|
||||||
m->owner_password.c_str(),
|
m->owner_password.c_str(),
|
||||||
m->r3_accessibility,
|
m->r3_accessibility,
|
||||||
|
@ -365,7 +365,7 @@ QPDFWriter::setPCLm(bool val)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
QPDFWriter::setR2EncryptionParameters(
|
QPDFWriter::setR2EncryptionParametersInsecure(
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
bool allow_print,
|
bool allow_print,
|
||||||
@ -391,7 +391,7 @@ QPDFWriter::setR2EncryptionParameters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
QPDFWriter::setR3EncryptionParameters(
|
QPDFWriter::setR3EncryptionParametersInsecure(
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
bool allow_accessibility,
|
bool allow_accessibility,
|
||||||
@ -419,7 +419,7 @@ QPDFWriter::setR3EncryptionParameters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
QPDFWriter::setR4EncryptionParameters(
|
QPDFWriter::setR4EncryptionParametersInsecure(
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
bool allow_accessibility,
|
bool allow_accessibility,
|
||||||
|
@ -674,7 +674,7 @@ qpdf_set_preserve_encryption(qpdf_data qpdf, QPDF_BOOL value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
qpdf_set_r2_encryption_parameters(
|
qpdf_set_r2_encryption_parameters_insecure(
|
||||||
qpdf_data qpdf,
|
qpdf_data qpdf,
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
@ -683,8 +683,8 @@ qpdf_set_r2_encryption_parameters(
|
|||||||
QPDF_BOOL allow_extract,
|
QPDF_BOOL allow_extract,
|
||||||
QPDF_BOOL allow_annotate)
|
QPDF_BOOL allow_annotate)
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "qpdf-c called qpdf_set_r2_encryption_parameters");
|
QTC::TC("qpdf", "qpdf-c called qpdf_set_r2_encryption_parameters_insecure");
|
||||||
qpdf->qpdf_writer->setR2EncryptionParameters(
|
qpdf->qpdf_writer->setR2EncryptionParametersInsecure(
|
||||||
user_password,
|
user_password,
|
||||||
owner_password,
|
owner_password,
|
||||||
allow_print != QPDF_FALSE,
|
allow_print != QPDF_FALSE,
|
||||||
@ -694,7 +694,7 @@ qpdf_set_r2_encryption_parameters(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
qpdf_set_r3_encryption_parameters2(
|
qpdf_set_r3_encryption_parameters_insecure(
|
||||||
qpdf_data qpdf,
|
qpdf_data qpdf,
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
@ -706,8 +706,8 @@ qpdf_set_r3_encryption_parameters2(
|
|||||||
QPDF_BOOL allow_modify_other,
|
QPDF_BOOL allow_modify_other,
|
||||||
enum qpdf_r3_print_e print)
|
enum qpdf_r3_print_e print)
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "qpdf-c called qpdf_set_r3_encryption_parameters");
|
QTC::TC("qpdf", "qpdf-c called qpdf_set_r3_encryption_parameters_insecure");
|
||||||
qpdf->qpdf_writer->setR3EncryptionParameters(
|
qpdf->qpdf_writer->setR3EncryptionParametersInsecure(
|
||||||
user_password,
|
user_password,
|
||||||
owner_password,
|
owner_password,
|
||||||
allow_accessibility != QPDF_FALSE,
|
allow_accessibility != QPDF_FALSE,
|
||||||
@ -720,7 +720,7 @@ qpdf_set_r3_encryption_parameters2(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
qpdf_set_r4_encryption_parameters2(
|
qpdf_set_r4_encryption_parameters_insecure(
|
||||||
qpdf_data qpdf,
|
qpdf_data qpdf,
|
||||||
char const* user_password,
|
char const* user_password,
|
||||||
char const* owner_password,
|
char const* owner_password,
|
||||||
@ -734,8 +734,8 @@ qpdf_set_r4_encryption_parameters2(
|
|||||||
QPDF_BOOL encrypt_metadata,
|
QPDF_BOOL encrypt_metadata,
|
||||||
QPDF_BOOL use_aes)
|
QPDF_BOOL use_aes)
|
||||||
{
|
{
|
||||||
QTC::TC("qpdf", "qpdf-c called qpdf_set_r4_encryption_parameters");
|
QTC::TC("qpdf", "qpdf-c called qpdf_set_r4_encryption_parameters_insecure");
|
||||||
qpdf->qpdf_writer->setR4EncryptionParameters(
|
qpdf->qpdf_writer->setR4EncryptionParametersInsecure(
|
||||||
user_password,
|
user_password,
|
||||||
owner_password,
|
owner_password,
|
||||||
allow_accessibility != QPDF_FALSE,
|
allow_accessibility != QPDF_FALSE,
|
||||||
|
@ -470,12 +470,18 @@ Related Options
|
|||||||
option is necessary to create 40-bit files or 128-bit files that
|
option is necessary to create 40-bit files or 128-bit files that
|
||||||
use RC4 encryption.
|
use RC4 encryption.
|
||||||
|
|
||||||
Starting with version 10.4, qpdf issues warnings when requested to
|
Encrypted PDF files using 40-bit keys or 128-bit keys without AES
|
||||||
create files using RC4 encryption. This option suppresses those
|
use the insecure *RC4* encryption algorithm. Starting with version
|
||||||
warnings. In future versions of qpdf, qpdf will refuse to create
|
11.0, qpdf's default behavior is to refuse to write files using RC4
|
||||||
files with weak cryptography when this flag is not given. See
|
encryption. Use this option to allow creation of such files. In
|
||||||
|
versions 10.4 through 10.6, attempting to create weak encrypted
|
||||||
|
files was a warning, rather than an error, without this flag. See
|
||||||
:ref:`weak-crypto` for additional details.
|
:ref:`weak-crypto` for additional details.
|
||||||
|
|
||||||
|
No check is performed for weak crypto when preserving encryption
|
||||||
|
parameters from or copying encryption parameters from other files.
|
||||||
|
The rationale for this is discussed in :ref:`weak-crypto`.
|
||||||
|
|
||||||
.. qpdf:option:: --keep-files-open=[y|n]
|
.. qpdf:option:: --keep-files-open=[y|n]
|
||||||
|
|
||||||
.. help: manage keeping multiple files open
|
.. help: manage keeping multiple files open
|
||||||
@ -741,6 +747,9 @@ Related Options
|
|||||||
file without having to manual specify all the individual settings.
|
file without having to manual specify all the individual settings.
|
||||||
See also :qpdf:ref:`--decrypt`.
|
See also :qpdf:ref:`--decrypt`.
|
||||||
|
|
||||||
|
Checks for weak cryptographic algorithms are intentionally not made
|
||||||
|
by this operation. See :ref:`weak-crypto` for the rationale.
|
||||||
|
|
||||||
.. qpdf:option:: --encryption-file-password=password
|
.. qpdf:option:: --encryption-file-password=password
|
||||||
|
|
||||||
.. help: supply password for --copy-encryption
|
.. help: supply password for --copy-encryption
|
||||||
|
@ -62,6 +62,10 @@ For a detailed list of changes, please see the file
|
|||||||
- The default json output version when :qpdf:ref:`--json` is
|
- The default json output version when :qpdf:ref:`--json` is
|
||||||
specified has been changed from ``1`` to ``latest``.
|
specified has been changed from ``1`` to ``latest``.
|
||||||
|
|
||||||
|
- The :qpdf:ref:`--allow-weak-crypto` flag is now mandatory when
|
||||||
|
explicitly creating files with weak cryptographic algorithms.
|
||||||
|
See :ref:`weak-crypto` for a discussion.
|
||||||
|
|
||||||
- API: breaking changes
|
- API: breaking changes
|
||||||
|
|
||||||
- Remove
|
- Remove
|
||||||
@ -73,6 +77,19 @@ For a detailed list of changes, please see the file
|
|||||||
``QPDFNumberTreeObjectHelper`` constructors that don't take a
|
``QPDFNumberTreeObjectHelper`` constructors that don't take a
|
||||||
``QPDF&`` argument.
|
``QPDF&`` argument.
|
||||||
|
|
||||||
|
- Intentionally break API to call attention to operations that
|
||||||
|
write files with insecure encryption:
|
||||||
|
|
||||||
|
- Remove pre qpdf-8.4.0 encryption API methods from ``QPDFWriter``
|
||||||
|
and their corresponding C API functions
|
||||||
|
|
||||||
|
- Add ``Insecure`` to the names of some ``QPDFWriter`` methods
|
||||||
|
and ``_insecure`` to the names of some C API functions without
|
||||||
|
otherwise changing their behavior
|
||||||
|
|
||||||
|
- See :ref:`breaking-crypto-api` for specific details, and see
|
||||||
|
:ref:`weak-crypto` for a general discussion.
|
||||||
|
|
||||||
- Library Enhancements
|
- Library Enhancements
|
||||||
|
|
||||||
- Support for more fluent programming with ``QPDFObjectHandle``.
|
- Support for more fluent programming with ``QPDFObjectHandle``.
|
||||||
|
@ -3,24 +3,55 @@
|
|||||||
Weak Cryptography
|
Weak Cryptography
|
||||||
=================
|
=================
|
||||||
|
|
||||||
Start with version 10.4, qpdf is taking steps to reduce the likelihood
|
For help with compiler errors in qpdf 11.0 or newer, see
|
||||||
of a user *accidentally* creating PDF files with insecure cryptography
|
:ref:`breaking-crypto-api`.
|
||||||
but will continue to allow creation of such files indefinitely with
|
|
||||||
explicit acknowledgment.
|
|
||||||
|
|
||||||
The PDF file format makes use of RC4, which is known to be a weak
|
Since 2006, the PDF specification has offered ways to create encrypted
|
||||||
cryptography algorithm, and MD5, which is a weak hashing algorithm. In
|
PDF files without using weak cryptography, though it took a few years
|
||||||
version 10.4, qpdf generates warnings for some (but not all) cases of
|
for many PDF readers and writers to catch up. It is still necessary to
|
||||||
writing files with weak cryptography when invoked from the command-line.
|
support weak encryption algorithms to read encrypted PDF files that
|
||||||
These warnings can be suppressed using the
|
were created using weak encryption algorithms, including all PDF files
|
||||||
:qpdf:ref:`--allow-weak-crypto` option.
|
created before the modern formats were introduced or widely supported.
|
||||||
|
|
||||||
It is planned for qpdf version 11 to be stricter, making it an error to
|
Starting with version 10.4, qpdf began taking steps to reduce the
|
||||||
write files with insecure cryptography from the command-line tool in
|
likelihood of a user *accidentally* creating PDF files with insecure
|
||||||
most cases without specifying the
|
cryptography but will continue to allow creation of such files
|
||||||
:qpdf:ref:`--allow-weak-crypto` flag and also to require
|
indefinitely with explicit acknowledgment. The restrictions on use of
|
||||||
explicit steps when using the C++ library to enable use of insecure
|
weak cryptography were made stricter with qpdf 11.
|
||||||
cryptography.
|
|
||||||
|
Definition of Weak Cryptographic Algorithm
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
We divide weak cryptographic algorithms into two categories: weak
|
||||||
|
encryption and weak hashing. Encryption is encoding data such that a
|
||||||
|
key of some sort is required to decode it. Hashing is creating a short
|
||||||
|
value from data in such a way that it is extremely improbable to find
|
||||||
|
two documents with the same hash (known has a hash collision) and
|
||||||
|
extremely difficult to intentionally create a document with a specific
|
||||||
|
hash or two documents with the same hash.
|
||||||
|
|
||||||
|
When we say that an encryption algorithm is weak, we either mean that
|
||||||
|
a mathematical flaw has been discovered that makes it inherently
|
||||||
|
crackable or that it is sufficiently simple that modern computer
|
||||||
|
technology makes it possible to use "brute force" to crack. For
|
||||||
|
example, when 40-bit keys were originally introduced, it wasn't
|
||||||
|
practical to consider trying all possible keys, but today such a thing
|
||||||
|
is possible.
|
||||||
|
|
||||||
|
When we say that a hashing algorithm is weak, we mean that, either
|
||||||
|
because of mathematical flaw or insufficient complexity, it is
|
||||||
|
computationally feasible to intentionally construct a hash collision.
|
||||||
|
|
||||||
|
While weak encryption should always be avoided, there are cases in
|
||||||
|
which it is safe to use a weak hashing algorithm when security is not
|
||||||
|
a factor. For example, a weak hashing algorithm should not be used as
|
||||||
|
the only mechanism to test whether a file has been tampered with. In
|
||||||
|
other words, you can't use a weak hash as a digital signature. There
|
||||||
|
is no harm, however, in using a weak hash as a way to sort or index
|
||||||
|
documents as long as hash collisions are tolerated. It is also common
|
||||||
|
to use weak hashes as checksums, which are often used a check that a
|
||||||
|
file wasn't damanged in transit or storage, though for true integrity,
|
||||||
|
a strong hash would be better.
|
||||||
|
|
||||||
Note that qpdf must always retain support for weak cryptographic
|
Note that qpdf must always retain support for weak cryptographic
|
||||||
algorithms since this is required for reading older PDF files that use
|
algorithms since this is required for reading older PDF files that use
|
||||||
@ -31,3 +62,135 @@ since these are sometimes needed to test or work with older versions of
|
|||||||
software. Even if other cryptography libraries drop support for RC4 or
|
software. Even if other cryptography libraries drop support for RC4 or
|
||||||
MD5, qpdf can always fall back to its internal implementations of those
|
MD5, qpdf can always fall back to its internal implementations of those
|
||||||
algorithms, so they are not going to disappear from qpdf.
|
algorithms, so they are not going to disappear from qpdf.
|
||||||
|
|
||||||
|
Uses of Weak Encryption in qpdf
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
When PDF files are encrypted using 40-bit encryption or 128-bit
|
||||||
|
encryption without AES, then the weak *RC4* algorithm is used. You can
|
||||||
|
avoid using weak encryption in qpdf by always using 256-bit
|
||||||
|
encryption. Unless you are trying to create files that need to be
|
||||||
|
opened with PDF readers from before about 2010 (by which time most
|
||||||
|
readers had added support for the stronger encryption algorithms) or
|
||||||
|
are creating insecure files explicitly for testing or some similar
|
||||||
|
purpose, there is no reason to use anything other than 256-bit
|
||||||
|
encryption.
|
||||||
|
|
||||||
|
By default, qpdf refuses to write a file that uses weak encryption.
|
||||||
|
You can explicitly allow this by specifying the
|
||||||
|
:qpdf:ref:`--allow-weak-crypto` option.
|
||||||
|
|
||||||
|
In qpdf 11, all library methods that could potentially cause files to
|
||||||
|
be written with weak encryption were deprecated, and methods to enable
|
||||||
|
weak encryption were either given explicit names indicating this or
|
||||||
|
take required arguments to enable the insecure behavior.
|
||||||
|
|
||||||
|
There is one exception: when encryption parameters are copied from the
|
||||||
|
input file or another file to the output file, there is no prohibition
|
||||||
|
or even warning against using insecure encryption. The reason is that
|
||||||
|
many qpdf operations simply preserve whatever encryption is there, and
|
||||||
|
requiring confirmation to *preserve* insecure encryption would cause
|
||||||
|
qpdf to break when non-encryption-related operations were performed on
|
||||||
|
files that happened to be encrypted. Failing or generating warnings in
|
||||||
|
this case would likely have the effect of making people use the
|
||||||
|
:qpdf:ref:`--allow-weak-crypto` option blindly, which would be worse
|
||||||
|
than just letting those files go so that explicit, conscious selection
|
||||||
|
of weak crypto would be more likely to be noticed. Why, you might ask,
|
||||||
|
does this apply to :qpdf:ref:`--copy-encryption` as well as to the
|
||||||
|
default behavior preserving encryption? The answer is that
|
||||||
|
:qpdf:ref:`--copy-encryption` works with an unencrypted file as input,
|
||||||
|
which enables workflows where one may start with a file, decrypt it
|
||||||
|
*just in case*, perform a series of operations, and then reapply the
|
||||||
|
original encryption, *if any*. Also, one may have a template used for
|
||||||
|
encryption that one may apply to a variety of output files, and it
|
||||||
|
would be annoying to be warned about it for every output file.
|
||||||
|
|
||||||
|
Uses of Weak Hashing In QPDF
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
The PDF specification makes use the weak *MD5* hashing algorithm in
|
||||||
|
several places. While it is used in the encryption algorithms,
|
||||||
|
breaking MD5 would not be adequate to crack an encrypted file when
|
||||||
|
256-bit encryption is in use, so using 256-bit encryption is adequate
|
||||||
|
for avoiding the use of MD5 for anything security-sensitive.
|
||||||
|
|
||||||
|
MD5 is used in the following non-security-sensitive ways:
|
||||||
|
|
||||||
|
- Generation of the document ID. The document ID is an input parameter
|
||||||
|
to the document encryption but is not itself considered to be
|
||||||
|
secure. They are supposed to be unique, but they are not
|
||||||
|
tamper-resistent in non-encrypted PDF files, and hash collisions
|
||||||
|
must be tolerated.
|
||||||
|
|
||||||
|
The PDF specification recommends but does not require the use of MD5
|
||||||
|
in generation of document IDs. Usually there is also a random
|
||||||
|
component to document ID generation. There is a qpdf-specific
|
||||||
|
feature of generating a *deterministic ID* (see
|
||||||
|
:qpdf:ref:`--deterministic-id`) which also uses MD5. While it would
|
||||||
|
certainly be possible to change the deterministic ID algorithm to
|
||||||
|
not use MD5, doing so would break all previous deterministic IDs
|
||||||
|
(which would render the feature useless for many cases) and would
|
||||||
|
offer very little benefit since even a securely generated document
|
||||||
|
ID is not itself a security-sensitive value.
|
||||||
|
|
||||||
|
- Checksums in embedded file streams -- the PDF specification
|
||||||
|
specifies the use of MD5.
|
||||||
|
|
||||||
|
It is therefore not possible completely avoid the use of MD5 with
|
||||||
|
qpdf, but as long as you are using 256-bit encryption, it is not used
|
||||||
|
in a securty-sensitive fashion.
|
||||||
|
|
||||||
|
.. _breaking-crypto-api:
|
||||||
|
|
||||||
|
API-Breaking Changes in qpdf 11.0
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
In qpdf 11, several deprecated functions and methods were removed.
|
||||||
|
These methods provided an incomplete API. Alternatives were added in
|
||||||
|
qpdf 8.4.0. The removed functions are
|
||||||
|
|
||||||
|
- C API: ``qpdf_set_r3_encryption_parameters``,
|
||||||
|
``qpdf_set_r4_encryption_parameters``,
|
||||||
|
``qpdf_set_r5_encryption_parameters``,
|
||||||
|
``qpdf_set_r6_encryption_parameters``
|
||||||
|
|
||||||
|
- ``QPDFWriter``: overloaded versions of these methods with fewer
|
||||||
|
arguments: ``setR3EncryptionParameters``,
|
||||||
|
``setR4EncryptionParameters``, ``setR5EncryptionParameters``, and
|
||||||
|
``setR6EncryptionParameters``
|
||||||
|
|
||||||
|
Additionally, remaining functions/methods had their names changed to
|
||||||
|
signal that they are insecure and to force developers to make a
|
||||||
|
decision. If you intentionally want to continue to use insecure
|
||||||
|
cryptographic algorithms and create insecure files, you can change
|
||||||
|
your code just add ``_insecure`` or ``Insecure`` to the end of the
|
||||||
|
function as needed. (Note the disappearance of ``2`` in some of the C
|
||||||
|
functions as well.) Better, you should migrate your code to use more
|
||||||
|
secure encryption as documented in :file:`QPDFWriter.hh`. Use the
|
||||||
|
``R6`` methods (or their corresponding C functions) to create files
|
||||||
|
with 256-bit encryption.
|
||||||
|
|
||||||
|
.. list-table:: Renamed Functions
|
||||||
|
:widths: 50 50
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
- - Old Name
|
||||||
|
- New Name
|
||||||
|
|
||||||
|
- - qpdf_set_r2_encryption_parameters
|
||||||
|
- qpdf_set_r2_encryption_parameters_insecure
|
||||||
|
|
||||||
|
- - qpdf_set_r3_encryption_parameters2
|
||||||
|
- qpdf_set_r3_encryption_parameters_insecure
|
||||||
|
|
||||||
|
- - qpdf_set_r4_encryption_parameters2
|
||||||
|
- qpdf_set_r2_encryption_parameters_insecure
|
||||||
|
|
||||||
|
- - QPDFWriter::setR2EncryptionParameters
|
||||||
|
- QPDFWriter::setR2EncryptionParametersInsecure
|
||||||
|
|
||||||
|
- - QPDFWriter::setR3EncryptionParameters
|
||||||
|
- QPDFWriter::setR3EncryptionParametersInsecure
|
||||||
|
|
||||||
|
- - QPDFWriter::setR4EncryptionParameters
|
||||||
|
- QPDFWriter::setR4EncryptionParametersInsecure
|
||||||
|
@ -320,7 +320,7 @@ test11(
|
|||||||
qpdf_read(qpdf, infile, password);
|
qpdf_read(qpdf, infile, password);
|
||||||
qpdf_init_write(qpdf, outfile);
|
qpdf_init_write(qpdf, outfile);
|
||||||
qpdf_set_static_ID(qpdf, QPDF_TRUE);
|
qpdf_set_static_ID(qpdf, QPDF_TRUE);
|
||||||
qpdf_set_r2_encryption_parameters(
|
qpdf_set_r2_encryption_parameters_insecure(
|
||||||
qpdf, "user1", "owner1", QPDF_FALSE, QPDF_TRUE, QPDF_TRUE, QPDF_TRUE);
|
qpdf, "user1", "owner1", QPDF_FALSE, QPDF_TRUE, QPDF_TRUE, QPDF_TRUE);
|
||||||
qpdf_write(qpdf);
|
qpdf_write(qpdf);
|
||||||
report_errors();
|
report_errors();
|
||||||
@ -336,7 +336,7 @@ test12(
|
|||||||
qpdf_read(qpdf, infile, password);
|
qpdf_read(qpdf, infile, password);
|
||||||
qpdf_init_write(qpdf, outfile);
|
qpdf_init_write(qpdf, outfile);
|
||||||
qpdf_set_static_ID(qpdf, QPDF_TRUE);
|
qpdf_set_static_ID(qpdf, QPDF_TRUE);
|
||||||
qpdf_set_r3_encryption_parameters2(
|
qpdf_set_r3_encryption_parameters_insecure(
|
||||||
qpdf,
|
qpdf,
|
||||||
"user2",
|
"user2",
|
||||||
"owner2",
|
"owner2",
|
||||||
@ -397,7 +397,7 @@ test15(
|
|||||||
qpdf_init_write(qpdf, outfile);
|
qpdf_init_write(qpdf, outfile);
|
||||||
qpdf_set_static_ID(qpdf, QPDF_TRUE);
|
qpdf_set_static_ID(qpdf, QPDF_TRUE);
|
||||||
qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
|
qpdf_set_static_aes_IV(qpdf, QPDF_TRUE);
|
||||||
qpdf_set_r4_encryption_parameters2(
|
qpdf_set_r4_encryption_parameters_insecure(
|
||||||
qpdf,
|
qpdf,
|
||||||
"user2",
|
"user2",
|
||||||
"owner2",
|
"owner2",
|
||||||
|
@ -130,8 +130,8 @@ qpdf-c called qpdf_set_qdf_mode 0
|
|||||||
qpdf-c called qpdf_set_static_ID 0
|
qpdf-c called qpdf_set_static_ID 0
|
||||||
qpdf-c called qpdf_set_suppress_original_object_IDs 0
|
qpdf-c called qpdf_set_suppress_original_object_IDs 0
|
||||||
qpdf-c called qpdf_set_preserve_encryption 0
|
qpdf-c called qpdf_set_preserve_encryption 0
|
||||||
qpdf-c called qpdf_set_r2_encryption_parameters 0
|
qpdf-c called qpdf_set_r2_encryption_parameters_insecure 0
|
||||||
qpdf-c called qpdf_set_r3_encryption_parameters 0
|
qpdf-c called qpdf_set_r3_encryption_parameters_insecure 0
|
||||||
qpdf-c called qpdf_set_linearization 0
|
qpdf-c called qpdf_set_linearization 0
|
||||||
qpdf-c called qpdf_write 1
|
qpdf-c called qpdf_write 1
|
||||||
qpdf-c called qpdf_allow_accessibility 0
|
qpdf-c called qpdf_allow_accessibility 0
|
||||||
@ -158,7 +158,7 @@ QPDF_encryption cleartext metadata 0
|
|||||||
QPDF_encryption aes decode stream 0
|
QPDF_encryption aes decode stream 0
|
||||||
QPDFWriter forcing object stream disable 0
|
QPDFWriter forcing object stream disable 0
|
||||||
QPDFWriter forced version disabled encryption 0
|
QPDFWriter forced version disabled encryption 0
|
||||||
qpdf-c called qpdf_set_r4_encryption_parameters 0
|
qpdf-c called qpdf_set_r4_encryption_parameters_insecure 0
|
||||||
qpdf-c called qpdf_set_static_aes_IV 0
|
qpdf-c called qpdf_set_static_aes_IV 0
|
||||||
QPDF_encryption stream crypt filter 0
|
QPDF_encryption stream crypt filter 0
|
||||||
QPDF ERR object stream with wrong type 0
|
QPDF ERR object stream with wrong type 0
|
||||||
|
Loading…
Reference in New Issue
Block a user