Commit 734ac1e1d2b3ce10a2da1a7e736a30bdf0bc5cf8

Authored by Jay Berkenbilt
1 parent 70ae58c0

deal with stream-specific crypt filters

git-svn-id: svn+q:///qpdf/trunk@827 71b93d88-0707-0410-a8cf-f5a4172ac649
1 2.1 1 2.1
2 === 2 ===
3 3
4 - * Really need to handle /Crypt filter for Metadata. Search for crypt  
5 - below.  
6 -  
7 * Update documentation to reflect new command line flags and any 4 * Update documentation to reflect new command line flags and any
8 other relevant changes. Should read through ChangeLog and the 5 other relevant changes. Should read through ChangeLog and the
9 manual before releasing 2.1. 6 manual before releasing 2.1.
@@ -83,10 +80,13 @@ General @@ -83,10 +80,13 @@ General
83 filters. There is an example in the spec of using a crypt filter 80 filters. There is an example in the spec of using a crypt filter
84 on a metadata stream. 81 on a metadata stream.
85 82
86 - When we write encrypted files, we must remember to omit any  
87 - encryption filter settings from original streams.  
88 -  
89 - We need a way to test this. 83 + For now, we notice /Crypt filters and decode parameters consistent
  84 + with the example in the PDF specification, and the right thing
  85 + happens for metadata filters that happen to be uncompressed or
  86 + otherwise compressed in a way we can filter. This should handle
  87 + all normal cases, but it's more or less just a guess since I don't
  88 + have any test files that actually use stream-specific crypt filters
  89 + in them.
90 90
91 * The second xref stream for linearized files has to be padded only 91 * The second xref stream for linearized files has to be padded only
92 because we need file_size as computed in pass 1 to be accurate. If 92 because we need file_size as computed in pass 1 to be accurate. If
include/qpdf/QPDFObjectHandle.hh
@@ -77,14 +77,14 @@ class DLL_EXPORT QPDFObjectHandle @@ -77,14 +77,14 @@ class DLL_EXPORT QPDFObjectHandle
77 bool isNumber(); 77 bool isNumber();
78 double getNumericValue(); 78 double getNumericValue();
79 79
80 - // Methods for name objects 80 + // Methods for name objects; see also name and array objects
81 std::string getName(); 81 std::string getName();
82 82
83 // Methods for string objects 83 // Methods for string objects
84 std::string getStringValue(); 84 std::string getStringValue();
85 std::string getUTF8Value(); 85 std::string getUTF8Value();
86 86
87 - // Methods for array objects 87 + // Methods for array objects; see also name and array objects
88 int getArrayNItems(); 88 int getArrayNItems();
89 QPDFObjectHandle getArrayItem(int n); 89 QPDFObjectHandle getArrayItem(int n);
90 90
@@ -93,6 +93,9 @@ class DLL_EXPORT QPDFObjectHandle @@ -93,6 +93,9 @@ class DLL_EXPORT QPDFObjectHandle
93 QPDFObjectHandle getKey(std::string const&); 93 QPDFObjectHandle getKey(std::string const&);
94 std::set<std::string> getKeys(); 94 std::set<std::string> getKeys();
95 95
  96 + // Methods for name and array objects
  97 + bool isOrHasName(std::string const&);
  98 +
96 // Mutator methods. Use with caution. 99 // Mutator methods. Use with caution.
97 100
98 // Recursively copy this object, making it direct. Throws an 101 // Recursively copy this object, making it direct. Throws an
libqpdf/QPDFObjectHandle.cc
@@ -258,6 +258,29 @@ QPDFObjectHandle::getKeys() @@ -258,6 +258,29 @@ QPDFObjectHandle::getKeys()
258 return dynamic_cast<QPDF_Dictionary*>(obj.getPointer())->getKeys(); 258 return dynamic_cast<QPDF_Dictionary*>(obj.getPointer())->getKeys();
259 } 259 }
260 260
  261 +// Array and Name accessors
  262 +bool
  263 +QPDFObjectHandle::isOrHasName(std::string const& value)
  264 +{
  265 + if (isName() && (getName() == value))
  266 + {
  267 + return true;
  268 + }
  269 + else if (isArray())
  270 + {
  271 + int n = getArrayNItems();
  272 + for (int i = 0; i < n; ++i)
  273 + {
  274 + QPDFObjectHandle item = getArrayItem(0);
  275 + if (item.isName() && (item.getName() == value))
  276 + {
  277 + return true;
  278 + }
  279 + }
  280 + }
  281 + return false;
  282 +}
  283 +
261 // Dictionary mutators 284 // Dictionary mutators
262 285
263 void 286 void
libqpdf/QPDF_Stream.cc
@@ -136,6 +136,13 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters, @@ -136,6 +136,13 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters,
136 filterable = false; 136 filterable = false;
137 } 137 }
138 } 138 }
  139 + else if (((key == "/Type") || (key == "/Name")) &&
  140 + decode_obj.getKey("/Type").isName() &&
  141 + (decode_obj.getKey("/Type").getName() ==
  142 + "/CryptFilterDecodeParms"))
  143 + {
  144 + // we handle this in decryptStream
  145 + }
139 else 146 else
140 { 147 {
141 filterable = false; 148 filterable = false;
@@ -212,7 +219,8 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters, @@ -212,7 +219,8 @@ QPDF_Stream::filterable(std::vector&lt;std::string&gt;&amp; filters,
212 iter != filters.end(); ++iter) 219 iter != filters.end(); ++iter)
213 { 220 {
214 std::string const& filter = *iter; 221 std::string const& filter = *iter;
215 - if (! ((filter == "/FlateDecode") || 222 + if (! ((filter == "/Crypt") ||
  223 + (filter == "/FlateDecode") ||
216 (filter == "/LZWDecode") || 224 (filter == "/LZWDecode") ||
217 (filter == "/ASCII85Decode") || 225 (filter == "/ASCII85Decode") ||
218 (filter == "/ASCIIHexDecode"))) 226 (filter == "/ASCIIHexDecode")))
@@ -266,7 +274,11 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter, @@ -266,7 +274,11 @@ QPDF_Stream::pipeStreamData(Pipeline* pipeline, bool filter,
266 iter != filters.rend(); ++iter) 274 iter != filters.rend(); ++iter)
267 { 275 {
268 std::string const& filter = *iter; 276 std::string const& filter = *iter;
269 - if (filter == "/FlateDecode") 277 + if (filter == "/Crypt")
  278 + {
  279 + // Ignore -- handled by pipeStreamData
  280 + }
  281 + else if (filter == "/FlateDecode")
270 { 282 {
271 if (predictor == 12) 283 if (predictor == 12)
272 { 284 {
libqpdf/QPDF_encryption.cc
@@ -600,18 +600,19 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation, @@ -600,18 +600,19 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
600 encryption_method_e method = e_unknown; 600 encryption_method_e method = e_unknown;
601 std::string method_source = "/StmF from /Encrypt dictionary"; 601 std::string method_source = "/StmF from /Encrypt dictionary";
602 602
603 - // NOTE: the section in the PDF specification on crypt filters  
604 - // seems to suggest that there might be a /Crypt key in  
605 - // /DecodeParms whose value is a crypt filter (.e.g., << /Name  
606 - // /StdCF >>), but implementation notes suggest this can only  
607 - // happen for metadata streams, and emperical observation  
608 - // suggests that they are otherwise ignored. Not having been  
609 - // able to find a sample file that uses crypt filters in any  
610 - // way other than /StrF and /StmF, I'm not really sure what to  
611 - // do about this. If we were to override the encryption on a  
612 - // per-stream basis using crypt filters, set method_source to  
613 - // something useful in the error message for unknown  
614 - // encryption methods (search for method_source). 603 + if (stream_dict.getKey("/Filter").isOrHasName("/Crypt") &&
  604 + stream_dict.getKey("/DecodeParms").isDictionary())
  605 + {
  606 + QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms");
  607 + if (decode_parms.getKey("/Type").isName() &&
  608 + (decode_parms.getKey("/Type").getName() ==
  609 + "/CryptFilterDecodeParms"))
  610 + {
  611 + QTC::TC("qpdf", "QPDF_encryption stream crypt filter");
  612 + method = interpretCF(decode_parms.getKey("/Name"));
  613 + method_source = "stream's Crypt decode parameters";
  614 + }
  615 + }
615 616
616 if (method == e_unknown) 617 if (method == e_unknown)
617 { 618 {
qpdf/qpdf.testcov
@@ -170,3 +170,4 @@ QPDFWriter forcing object stream disable 0 @@ -170,3 +170,4 @@ QPDFWriter forcing object stream disable 0
170 QPDFWriter forced version disabled encryption 0 170 QPDFWriter forced version disabled encryption 0
171 qpdf-c called qpdf_set_r4_encryption_parameters 0 171 qpdf-c called qpdf_set_r4_encryption_parameters 0
172 qpdf-c called qpdf_set_static_aes_IV 0 172 qpdf-c called qpdf_set_static_aes_IV 0
  173 +QPDF_encryption stream crypt filter 0
qpdf/qtest/qpdf.test
@@ -1079,7 +1079,7 @@ $td-&gt;runtest(&quot;make sure there is no xref stream&quot;, @@ -1079,7 +1079,7 @@ $td-&gt;runtest(&quot;make sure there is no xref stream&quot;,
1079 $td->NORMALIZE_NEWLINES); 1079 $td->NORMALIZE_NEWLINES);
1080 1080
1081 # Look at some actual V4 files 1081 # Look at some actual V4 files
1082 -$n_tests += 8; 1082 +$n_tests += 10;
1083 foreach my $d (['--force-V4', 'V4'], 1083 foreach my $d (['--force-V4', 'V4'],
1084 ['--cleartext-metadata', 'V4-clearmeta'], 1084 ['--cleartext-metadata', 'V4-clearmeta'],
1085 ['--use-aes=y', 'V4-aes'], 1085 ['--use-aes=y', 'V4-aes'],
@@ -1094,6 +1094,14 @@ foreach my $d ([&#39;--force-V4&#39;, &#39;V4&#39;], @@ -1094,6 +1094,14 @@ foreach my $d ([&#39;--force-V4&#39;, &#39;V4&#39;],
1094 {$td->FILE => "a.pdf"}, 1094 {$td->FILE => "a.pdf"},
1095 {$td->FILE => "$out.pdf"}); 1095 {$td->FILE => "$out.pdf"});
1096 } 1096 }
  1097 +# Crypt Filter
  1098 +$td->runtest("decrypt with crypt filter",
  1099 + {$td->COMMAND => "qpdf --decrypt --static-id" .
  1100 + " metadata-crypt-filter.pdf a.pdf"},
  1101 + {$td->STRING => "", $td->EXIT_STATUS => 0});
  1102 +$td->runtest("check output",
  1103 + {$td->FILE => 'a.pdf'},
  1104 + {$td->FILE => 'decrypted-crypt-filter.pdf'});
1097 1105
1098 show_ntests(); 1106 show_ntests();
1099 # ---------- 1107 # ----------
qpdf/qtest/qpdf/decrypted-crypt-filter.pdf 0 → 100644
No preview for this file type
qpdf/qtest/qpdf/metadata-crypt-filter.pdf 0 → 100644
No preview for this file type