mirror of
https://github.com/qpdf/qpdf.git
synced 2025-01-03 07:12:28 +00:00
more testing, bug fix for linearized aes encrypted files
git-svn-id: svn+q:///qpdf/trunk@824 71b93d88-0707-0410-a8cf-f5a4172ac649
This commit is contained in:
parent
94131116a9
commit
09175e4578
@ -1,5 +1,11 @@
|
|||||||
2009-10-18 Jay Berkenbilt <ejb@ql.org>
|
2009-10-18 Jay Berkenbilt <ejb@ql.org>
|
||||||
|
|
||||||
|
* If forcing version, disable object stream creation and/or
|
||||||
|
encryption if previous specifications are incompatible with new
|
||||||
|
version. It is still possible that PDF content, compression
|
||||||
|
schemes, etc., may be incompatible with the new version, but at
|
||||||
|
least this way, older viewers will at least have a chance.
|
||||||
|
|
||||||
* libqpdf/QPDFWriter.cc (unparseObject): avoid compressing
|
* libqpdf/QPDFWriter.cc (unparseObject): avoid compressing
|
||||||
Metadata streams if possible.
|
Metadata streams if possible.
|
||||||
|
|
||||||
|
56
TODO
56
TODO
@ -1,6 +1,9 @@
|
|||||||
2.1
|
2.1
|
||||||
===
|
===
|
||||||
|
|
||||||
|
* Really need to handle /Crypt filter for Metadata. Search for crypt
|
||||||
|
below.
|
||||||
|
|
||||||
* Update documentation to reflect new command line flags and any
|
* Update documentation to reflect new command line flags and any
|
||||||
other relevant changes. Should read through ChangeLog and the
|
other relevant changes. Should read through ChangeLog and the
|
||||||
manual before releasing 2.1.
|
manual before releasing 2.1.
|
||||||
@ -16,9 +19,6 @@
|
|||||||
* Add comments for the security functions that map them back to the
|
* Add comments for the security functions that map them back to the
|
||||||
items in Adobe's products.
|
items in Adobe's products.
|
||||||
|
|
||||||
* Have force version at least turn off object streams and maybe
|
|
||||||
change security settings?
|
|
||||||
|
|
||||||
* Add error codes to QPDFException. Change the error interface so
|
* Add error codes to QPDFException. Change the error interface so
|
||||||
that warnings and errors are pointers that can be queried using
|
that warnings and errors are pointers that can be queried using
|
||||||
more C API functions. We need a way to get a full string as well
|
more C API functions. We need a way to get a full string as well
|
||||||
@ -47,49 +47,7 @@
|
|||||||
|
|
||||||
- Update C API for R4 encryption
|
- Update C API for R4 encryption
|
||||||
|
|
||||||
- When we write encrypted files, we must remember to omit any
|
|
||||||
encryption filter settings from original streams.
|
|
||||||
|
|
||||||
- test various combinations with and without cleartext-metadata
|
|
||||||
and aes in compression tests
|
|
||||||
|
|
||||||
- figure out a way to test crypt filters defined on a stream
|
|
||||||
|
|
||||||
- test combinations of linearization and v4 encryption
|
|
||||||
|
|
||||||
- would be nice to test strings and streams with different
|
|
||||||
encryption types, but without sample data, we'd have to write
|
|
||||||
them ourselves which is not that useful
|
|
||||||
|
|
||||||
- figure out how to look at the metadata so I can tell whether
|
|
||||||
/EncryptMetadata is working the way it's supposed to
|
|
||||||
|
|
||||||
- Do something with embedded files, but what and how?
|
|
||||||
|
|
||||||
- General notes:
|
|
||||||
|
|
||||||
/CF - keys are crypt filter names, values are are crypt
|
|
||||||
dictionaries
|
|
||||||
|
|
||||||
Individual streams may also have crypt filters. Filter type
|
|
||||||
/Crypt; /DecodeParms must contain a Crypt filter decode
|
|
||||||
parameters dictionary whose /Name entry specifies the particular
|
|
||||||
filter to be used. If /Name is missing, use /Identity.
|
|
||||||
/DecodeParms << /Crypt << /Name /XYZ >> >> where /XYZ is
|
|
||||||
/Identity or a key in /CF.
|
|
||||||
|
|
||||||
/Identity means not to encrypt.
|
|
||||||
|
|
||||||
Crypt Dictionaries
|
|
||||||
|
|
||||||
/Type (optional) /CryptFilter
|
|
||||||
/CFM:
|
|
||||||
/V2 - use rc4
|
|
||||||
/AESV2 - use aes
|
|
||||||
/Length - supposed to be key length, but the one file I have
|
|
||||||
has a bogus value for it, so I'm ignoring it.
|
|
||||||
|
|
||||||
We will ignore remaining fields and values.
|
|
||||||
|
|
||||||
2.2
|
2.2
|
||||||
===
|
===
|
||||||
@ -127,7 +85,13 @@ General
|
|||||||
crypt filters, and there are already special cases in the code to
|
crypt filters, and there are already special cases in the code to
|
||||||
handle those. Most likely, it won't be a problem, but someday
|
handle those. Most likely, it won't be a problem, but someday
|
||||||
someone may find a file that qpdf doesn't work on because of crypt
|
someone may find a file that qpdf doesn't work on because of crypt
|
||||||
filters.
|
filters. There is an example in the spec of using a crypt filter
|
||||||
|
on a metadata stream.
|
||||||
|
|
||||||
|
When we write encrypted files, we must remember to omit any
|
||||||
|
encryption filter settings from original streams.
|
||||||
|
|
||||||
|
We need a way to test this.
|
||||||
|
|
||||||
* The second xref stream for linearized files has to be padded only
|
* The second xref stream for linearized files has to be padded only
|
||||||
because we need file_size as computed in pass 1 to be accurate. If
|
because we need file_size as computed in pass 1 to be accurate. If
|
||||||
|
@ -98,6 +98,12 @@ class DLL_EXPORT QPDFWriter
|
|||||||
// you are sure the PDF file in question has no features of newer
|
// you are sure the PDF file in question has no features of newer
|
||||||
// versions of PDF or if you are willing to create files that old
|
// versions of PDF or if you are willing to create files that old
|
||||||
// viewers may try to open but not be able to properly interpret.
|
// viewers may try to open but not be able to properly interpret.
|
||||||
|
// If any encryption has been applied to the document either
|
||||||
|
// explicitly or by preserving the encryption of the source
|
||||||
|
// document, forcing the PDF version to a value too low to support
|
||||||
|
// that type of encryption will explicitly disable decryption.
|
||||||
|
// Additionally, forcing to a version below 1.5 will disable
|
||||||
|
// object streams.
|
||||||
void forcePDFVersion(std::string const&);
|
void forcePDFVersion(std::string const&);
|
||||||
|
|
||||||
// Cause a static /ID value to be generated. Use only in test
|
// Cause a static /ID value to be generated. Use only in test
|
||||||
@ -193,6 +199,7 @@ class DLL_EXPORT QPDFWriter
|
|||||||
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,
|
||||||
r3_print_e print, r3_modify_e modify);
|
r3_print_e print, r3_modify_e modify);
|
||||||
|
void disableIncompatbleEncryption(float v);
|
||||||
void setEncryptionParameters(
|
void setEncryptionParameters(
|
||||||
char const* user_password, char const* owner_password,
|
char const* user_password, char const* owner_password,
|
||||||
int V, int R, int key_len, std::set<int>& bits_to_clear);
|
int V, int R, int key_len, std::set<int>& bits_to_clear);
|
||||||
|
@ -344,6 +344,52 @@ QPDFWriter::copyEncryptionParameters()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QPDFWriter::disableIncompatbleEncryption(float v)
|
||||||
|
{
|
||||||
|
if (! this->encrypted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool disable = false;
|
||||||
|
if (v < 1.3)
|
||||||
|
{
|
||||||
|
disable = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int V = atoi(encryption_dictionary["/V"].c_str());
|
||||||
|
int R = atoi(encryption_dictionary["/R"].c_str());
|
||||||
|
if (v < 1.4)
|
||||||
|
{
|
||||||
|
if ((V > 1) || (R > 2))
|
||||||
|
{
|
||||||
|
disable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (v < 1.5)
|
||||||
|
{
|
||||||
|
if ((V > 2) || (R > 3))
|
||||||
|
{
|
||||||
|
disable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (v < 1.6)
|
||||||
|
{
|
||||||
|
if (this->encrypt_use_aes)
|
||||||
|
{
|
||||||
|
disable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (disable)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFWriter forced version disabled encryption");
|
||||||
|
this->encrypted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
QPDFWriter::setEncryptionParametersInternal(
|
QPDFWriter::setEncryptionParametersInternal(
|
||||||
int V, int R, int key_len, long P,
|
int V, int R, int key_len, long P,
|
||||||
@ -965,7 +1011,7 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
|
|||||||
Buffer* buf = bufpl.getBuffer();
|
Buffer* buf = bufpl.getBuffer();
|
||||||
val = QPDF_String(
|
val = QPDF_String(
|
||||||
std::string((char*)buf->getBuffer(),
|
std::string((char*)buf->getBuffer(),
|
||||||
(size_t)buf->getSize())).unparse();
|
(size_t)buf->getSize())).unparse(true);
|
||||||
delete buf;
|
delete buf;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1423,6 +1469,17 @@ QPDFWriter::write()
|
|||||||
copyEncryptionParameters();
|
copyEncryptionParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (! this->forced_pdf_version.empty())
|
||||||
|
{
|
||||||
|
float v = atof(this->forced_pdf_version.c_str());
|
||||||
|
disableIncompatbleEncryption(v);
|
||||||
|
if (v < 1.5)
|
||||||
|
{
|
||||||
|
QTC::TC("qpdf", "QPDFWriter forcing object stream disable");
|
||||||
|
this->object_stream_mode = o_disable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this->qdf_mode || this->normalize_content ||
|
if (this->qdf_mode || this->normalize_content ||
|
||||||
(this->stream_data_mode == s_uncompress))
|
(this->stream_data_mode == s_uncompress))
|
||||||
{
|
{
|
||||||
|
@ -166,4 +166,5 @@ QPDF_encryption CFM AESV2 0
|
|||||||
QPDF_encryption aes decode string 0
|
QPDF_encryption aes decode string 0
|
||||||
QPDF_encryption cleartext metadata 0
|
QPDF_encryption cleartext metadata 0
|
||||||
QPDF_encryption aes decode stream 0
|
QPDF_encryption aes decode stream 0
|
||||||
QPDF_encryption stream crypt filter 0
|
QPDFWriter forcing object stream disable 0
|
||||||
|
QPDFWriter forced version disabled encryption 0
|
||||||
|
@ -998,14 +998,14 @@ $td->runtest("check linearization",
|
|||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
$td->runtest("linearize and encrypt file",
|
$td->runtest("linearize and encrypt file",
|
||||||
{$td->COMMAND =>
|
{$td->COMMAND =>
|
||||||
"qpdf --linearize --encrypt user owner 128 --" .
|
"qpdf --linearize --encrypt user owner 128 --use-aes=y --" .
|
||||||
" lin-special.pdf a.pdf"},
|
" lin-special.pdf a.pdf"},
|
||||||
{$td->STRING => "",
|
{$td->STRING => "",
|
||||||
$td->EXIT_STATUS => 0});
|
$td->EXIT_STATUS => 0});
|
||||||
$td->runtest("check encryption",
|
$td->runtest("check encryption",
|
||||||
{$td->COMMAND => "qpdf --show-encryption --password=owner a.pdf",
|
{$td->COMMAND => "qpdf --show-encryption --password=owner a.pdf",
|
||||||
$td->FILTER => "grep -v allowed"},
|
$td->FILTER => "grep -v allowed"},
|
||||||
{$td->STRING => "R = 3\nP = -4\nUser password = user\n",
|
{$td->STRING => "R = 4\nP = -4\nUser password = user\n",
|
||||||
$td->EXIT_STATUS => 0},
|
$td->EXIT_STATUS => 0},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
$td->runtest("check linearization",
|
$td->runtest("check linearization",
|
||||||
@ -1015,6 +1015,68 @@ $td->runtest("check linearization",
|
|||||||
$td->EXIT_STATUS => 0},
|
$td->EXIT_STATUS => 0},
|
||||||
$td->NORMALIZE_NEWLINES);
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
|
# Test AES encryption in various ways.
|
||||||
|
$n_tests += 14;
|
||||||
|
$td->runtest("encrypt with AES",
|
||||||
|
{$td->COMMAND => "qpdf --encrypt '' '' 128 --use-aes=y --" .
|
||||||
|
" enc-base.pdf a.pdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("check encryption",
|
||||||
|
{$td->COMMAND => "qpdf --show-encryption a.pdf",
|
||||||
|
$td->FILTER => "grep -v allowed"},
|
||||||
|
{$td->STRING => "R = 4\nP = -4\nUser password = \n",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("convert original to qdf",
|
||||||
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
||||||
|
" --qdf --min-version=1.6 enc-base.pdf a.qdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("convert encrypted to qdf",
|
||||||
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
||||||
|
" --qdf a.pdf b.qdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("compare files",
|
||||||
|
{$td->FILE => 'a.qdf'},
|
||||||
|
{$td->FILE => 'b.qdf'});
|
||||||
|
$td->runtest("linearize with AES and object streams",
|
||||||
|
{$td->COMMAND => "qpdf --encrypt '' '' 128 --use-aes=y --" .
|
||||||
|
" --linearize --object-streams=generate enc-base.pdf a.pdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("check encryption",
|
||||||
|
{$td->COMMAND => "qpdf --show-encryption a.pdf",
|
||||||
|
$td->FILTER => "grep -v allowed"},
|
||||||
|
{$td->STRING => "R = 4\nP = -4\nUser password = \n",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("linearize original",
|
||||||
|
{$td->COMMAND => "qpdf --linearize --object-streams=generate" .
|
||||||
|
" enc-base.pdf b.pdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("convert linearized original to qdf",
|
||||||
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
||||||
|
" --qdf --object-streams=generate --min-version=1.6" .
|
||||||
|
" b.pdf a.qdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("convert encrypted to qdf",
|
||||||
|
{$td->COMMAND => "qpdf --static-id --no-original-object-ids" .
|
||||||
|
" --qdf --object-streams=generate a.pdf b.qdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("compare files",
|
||||||
|
{$td->FILE => 'a.qdf'},
|
||||||
|
{$td->FILE => 'b.qdf'});
|
||||||
|
$td->runtest("force version on aes encrypted",
|
||||||
|
{$td->COMMAND => "qpdf --force-version=1.4 a.pdf b.pdf"},
|
||||||
|
{$td->STRING => "", $td->EXIT_STATUS => 0});
|
||||||
|
$td->runtest("check",
|
||||||
|
{$td->COMMAND => "qpdf --check b.pdf"},
|
||||||
|
{$td->FILE => "aes-forced-check.out",
|
||||||
|
$td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
$td->runtest("make sure there is no xref stream",
|
||||||
|
{$td->COMMAND => "grep /ObjStm b.pdf | wc -l"},
|
||||||
|
{$td->REGEXP => "\\s*0\\s*", $td->EXIT_STATUS => 0},
|
||||||
|
$td->NORMALIZE_NEWLINES);
|
||||||
|
|
||||||
show_ntests();
|
show_ntests();
|
||||||
# ----------
|
# ----------
|
||||||
$td->notify("--- Content Preservation Tests ---");
|
$td->notify("--- Content Preservation Tests ---");
|
||||||
|
5
qpdf/qtest/qpdf/aes-forced-check.out
Normal file
5
qpdf/qtest/qpdf/aes-forced-check.out
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
checking b.pdf
|
||||||
|
PDF Version: 1.4
|
||||||
|
File is not encrypted
|
||||||
|
File is not linearized
|
||||||
|
No errors found
|
Loading…
Reference in New Issue
Block a user