From 5c1d53fa67372eddb895aa8fa63e312c38cd9029 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Mon, 21 Nov 2016 14:39:07 +0900 Subject: [PATCH] Imported Upstream version 4.0.0 Change-Id: Ic246819002cfe4aa9660c4ca2b05add1b8dab626 Signed-off-by: DongHun Kwak --- ChangeLog | 143 ++ README | 29 +- README.maintainer | 12 +- TODO | 90 +- configure | 18 +- configure.ac | 2 +- doc/qpdf-manual.html | 215 ++- doc/qpdf-manual.pdf | Bin 146522 -> 159042 bytes include/qpdf/Buffer.hh | 2 +- include/qpdf/BufferInputSource.hh | 8 + include/qpdf/Constants.h | 2 +- include/qpdf/DLL.h | 2 +- include/qpdf/FileInputSource.hh | 8 + include/qpdf/InputSource.hh | 8 + include/qpdf/Pipeline.hh | 10 +- include/qpdf/Pl_Buffer.hh | 2 +- include/qpdf/Pl_Concatenate.hh | 8 + include/qpdf/Pl_Count.hh | 2 +- include/qpdf/Pl_Discard.hh | 2 +- include/qpdf/Pl_Flate.hh | 2 +- include/qpdf/Pl_StdioFile.hh | 2 +- include/qpdf/PointerHolder.hh | 2 +- include/qpdf/QPDF.hh | 89 +- include/qpdf/QPDFExc.hh | 2 +- include/qpdf/QPDFObject.hh | 2 +- include/qpdf/QPDFObjectHandle.hh | 2 +- include/qpdf/QPDFTokenizer.hh | 2 +- include/qpdf/QPDFWriter.hh | 50 +- include/qpdf/QPDFXRefEntry.hh | 2 +- include/qpdf/QTC.hh | 2 +- include/qpdf/QUtil.hh | 14 +- include/qpdf/Types.h | 8 + include/qpdf/qpdf-c.h | 28 +- ispell-words | 112 +- libqpdf.map | 2 +- libqpdf.pc.in | 1 + libqpdf/OffsetInputSource.cc | 61 + libqpdf/Pl_AES_PDF.cc | 112 +- libqpdf/Pl_SHA2.cc | 164 ++ libqpdf/QPDF.cc | 145 +- libqpdf/QPDFWriter.cc | 611 +++++- libqpdf/QPDF_Stream.cc | 250 ++- libqpdf/QPDF_encryption.cc | 801 ++++++-- libqpdf/QPDF_optimization.cc | 101 +- libqpdf/QUtil.cc | 39 + libqpdf/build.mk | 19 +- libqpdf/qpdf-c.cc | 48 +- libqpdf/qpdf/OffsetInputSource.hh | 29 + libqpdf/qpdf/Pl_AES_PDF.hh | 28 +- libqpdf/qpdf/Pl_SHA2.hh | 50 + libqpdf/qpdf/QPDF_Stream.hh | 3 + libqpdf/sha2.c | 690 +++++++ libqpdf/sha2big.c | 247 +++ libqpdf/sph/md_helper.c | 346 ++++ libqpdf/sph/sph_sha2.h | 378 ++++ libqpdf/sph/sph_types.h | 1976 ++++++++++++++++++++ libtests/aes.cc | 116 +- libtests/build.mk | 3 +- libtests/qtest/sha2.test | 18 + libtests/qtest/sha2/sha2.out | 9 + libtests/sha2.cc | 69 + make_dist | 1 + make_windows_releases | 2 + make_windows_releases-msvc | 1 + manual/qpdf-manual.xml | 289 ++- qpdf.spec | 2 +- qpdf/qpdf-ctest.c | 40 +- qpdf/qpdf.cc | 122 +- qpdf/qpdf.testcov | 25 +- qpdf/qtest/qpdf.test | 266 ++- qpdf/qtest/qpdf/V5R5.out | 20 + qpdf/qtest/qpdf/V5R6.out | 20 + qpdf/qtest/qpdf/attachments.out | 6 + qpdf/qtest/qpdf/c-decrypt-R5-with-user.pdf | Bin 0 -> 9731 bytes qpdf/qtest/qpdf/c-decrypt-R6-with-owner.pdf | Bin 0 -> 9731 bytes qpdf/qtest/qpdf/c-min-version.out | 4 + qpdf/qtest/qpdf/c-r5-in.pdf | Bin 0 -> 11195 bytes qpdf/qtest/qpdf/c-r5.out | 20 + qpdf/qtest/qpdf/c-r6-in.pdf | Bin 0 -> 11195 bytes qpdf/qtest/qpdf/c-r6.out | 20 + qpdf/qtest/qpdf/custom-pipeline.pdf | Bin 0 -> 799 bytes qpdf/qtest/qpdf/damaged-stream-c-check.out | 6 +- qpdf/qtest/qpdf/diff-encrypted | 2 +- qpdf/qtest/qpdf/enc-XI-R6,V5,O=master.pdf | Bin 0 -> 10933 bytes ...XI-R6,V5,U=attachment,encrypted-attachments.pdf | Bin 0 -> 17868 bytes qpdf/qtest/qpdf/enc-XI-R6,V5,U=view,O=master.pdf | Bin 0 -> 15670 bytes ...R6,V5,U=view,attachments,cleartext-metadata.pdf | Bin 0 -> 22400 bytes qpdf/qtest/qpdf/enc-XI-attachments-base.pdf | Bin 0 -> 20751 bytes qpdf/qtest/qpdf/enc-XI-base.pdf | Bin 0 -> 14041 bytes qpdf/qtest/qpdf/enc-XI-long-password.pdf | Bin 0 -> 10915 bytes qpdf/qtest/qpdf/extensions-adbe-force-1.3.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.6.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out | 4 + qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdf | Bin 0 -> 865 bytes qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf | 107 ++ qpdf/qtest/qpdf/extensions-adbe-force-1.8.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.3.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.6.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out | 4 + qpdf/qtest/qpdf/extensions-adbe-min-1.8.out | 4 + .../qtest/qpdf/extensions-adbe-other-force-1.3.out | 4 + .../qtest/qpdf/extensions-adbe-other-force-1.6.out | 4 + .../qpdf/extensions-adbe-other-force-1.7.1.out | 4 + .../qpdf/extensions-adbe-other-force-1.7.2.out | 4 + .../qpdf/extensions-adbe-other-force-1.7.3.out | 4 + .../qpdf/extensions-adbe-other-force-1.8.0.out | 4 + .../qpdf/extensions-adbe-other-force-1.8.2.out | 4 + .../qpdf/extensions-adbe-other-force-1.8.5.out | 4 + .../qpdf/extensions-adbe-other-force-1.8.5.pdf | Bin 0 -> 923 bytes .../qpdf/extensions-adbe-other-force-1.8.5.qdf | 111 ++ .../qtest/qpdf/extensions-adbe-other-force-1.8.out | 4 + qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out | 4 + qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out | 4 + .../qtest/qpdf/extensions-adbe-other-min-1.7.1.out | 4 + .../qtest/qpdf/extensions-adbe-other-min-1.7.2.out | 4 + .../qtest/qpdf/extensions-adbe-other-min-1.7.3.out | 4 + .../qtest/qpdf/extensions-adbe-other-min-1.8.0.out | 4 + .../qtest/qpdf/extensions-adbe-other-min-1.8.2.out | 4 + .../qtest/qpdf/extensions-adbe-other-min-1.8.5.out | 4 + qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out | 4 + qpdf/qtest/qpdf/extensions-adbe-other.pdf | 104 ++ qpdf/qtest/qpdf/extensions-adbe.pdf | 106 ++ qpdf/qtest/qpdf/extensions-none-force-1.3.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.6.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.7.1.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.7.2.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.7.3.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.8.0.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.8.2.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.8.5.out | 4 + qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdf | Bin 0 -> 865 bytes qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf | 107 ++ qpdf/qtest/qpdf/extensions-none-force-1.8.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.3.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.6.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.7.1.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.7.2.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.7.3.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.8.0.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.8.2.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.8.5.out | 4 + qpdf/qtest/qpdf/extensions-none-min-1.8.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.3.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.6.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.7.1.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.7.2.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.7.3.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.8.0.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.8.2.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.8.5.out | 4 + qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdf | Bin 0 -> 923 bytes qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf | 111 ++ qpdf/qtest/qpdf/extensions-other-force-1.8.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.3.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.6.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.7.1.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.7.2.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.7.3.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.8.0.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.8.2.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.8.5.out | 4 + qpdf/qtest/qpdf/extensions-other-min-1.8.out | 4 + qpdf/qtest/qpdf/extensions-other.pdf | 98 + qpdf/qtest/qpdf/good13.qdf | 46 +- qpdf/qtest/qpdf/good5.qdf | 50 +- qpdf/qtest/qpdf/good8.qdf | 50 +- qpdf/qtest/qpdf/leading-junk.out | 17 + qpdf/qtest/qpdf/leading-junk.pdf | Bin 0 -> 13670 bytes qpdf/qtest/qpdf/lin-special.disable.exp | Bin 3178 -> 3092 bytes qpdf/qtest/qpdf/lin-special.generate.exp | Bin 2808 -> 2849 bytes qpdf/qtest/qpdf/lin-special.preserve.exp | Bin 3178 -> 3092 bytes qpdf/qtest/qpdf/obj0-check.out | 6 +- qpdf/qtest/qpdf/object-stream.disable.exp | Bin 1310 -> 1285 bytes qpdf/qtest/qpdf/object-stream.generate.exp | Bin 1536 -> 1373 bytes qpdf/qtest/qpdf/object-stream.preserve.exp | Bin 1536 -> 1373 bytes qpdf/qtest/qpdf/test4-1.qdf | 58 +- qpdf/qtest/qpdf/unfilterable-with-crypt-after.out | 4 + qpdf/qtest/qpdf/unfilterable-with-crypt-before.out | 4 + qpdf/qtest/qpdf/unfilterable-with-crypt.pdf | Bin 0 -> 17868 bytes qpdf/qtest/qpdf/unreferenced-indirect-scalar.out | Bin 1402 -> 1405 bytes qpdf/qtest/qpdf/zero-offset.out | 5 + qpdf/qtest/qpdf/zero-offset.pdf | Bin 0 -> 80688 bytes qpdf/test_driver.cc | 163 +- 193 files changed, 8641 insertions(+), 838 deletions(-) create mode 100644 libqpdf/OffsetInputSource.cc create mode 100644 libqpdf/Pl_SHA2.cc create mode 100644 libqpdf/qpdf/OffsetInputSource.hh create mode 100644 libqpdf/qpdf/Pl_SHA2.hh create mode 100644 libqpdf/sha2.c create mode 100644 libqpdf/sha2big.c create mode 100644 libqpdf/sph/md_helper.c create mode 100644 libqpdf/sph/sph_sha2.h create mode 100644 libqpdf/sph/sph_types.h create mode 100644 libtests/qtest/sha2.test create mode 100644 libtests/qtest/sha2/sha2.out create mode 100644 libtests/sha2.cc create mode 100644 qpdf/qtest/qpdf/V5R5.out create mode 100644 qpdf/qtest/qpdf/V5R6.out create mode 100644 qpdf/qtest/qpdf/attachments.out create mode 100644 qpdf/qtest/qpdf/c-decrypt-R5-with-user.pdf create mode 100644 qpdf/qtest/qpdf/c-decrypt-R6-with-owner.pdf create mode 100644 qpdf/qtest/qpdf/c-min-version.out create mode 100644 qpdf/qtest/qpdf/c-r5-in.pdf create mode 100644 qpdf/qtest/qpdf/c-r5.out create mode 100644 qpdf/qtest/qpdf/c-r6-in.pdf create mode 100644 qpdf/qtest/qpdf/c-r6.out create mode 100644 qpdf/qtest/qpdf/custom-pipeline.pdf create mode 100644 qpdf/qtest/qpdf/enc-XI-R6,V5,O=master.pdf create mode 100644 qpdf/qtest/qpdf/enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf create mode 100644 qpdf/qtest/qpdf/enc-XI-R6,V5,U=view,O=master.pdf create mode 100644 qpdf/qtest/qpdf/enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf create mode 100644 qpdf/qtest/qpdf/enc-XI-attachments-base.pdf create mode 100644 qpdf/qtest/qpdf/enc-XI-base.pdf create mode 100644 qpdf/qtest/qpdf/enc-XI-long-password.pdf create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdf create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf create mode 100644 qpdf/qtest/qpdf/extensions-adbe-force-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-min-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.pdf create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-adbe-other.pdf create mode 100644 qpdf/qtest/qpdf/extensions-adbe.pdf create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdf create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf create mode 100644 qpdf/qtest/qpdf/extensions-none-force-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-none-min-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdf create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf create mode 100644 qpdf/qtest/qpdf/extensions-other-force-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.3.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.6.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.7.1.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.7.2.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.7.3.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.8.0.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.8.2.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.8.5.out create mode 100644 qpdf/qtest/qpdf/extensions-other-min-1.8.out create mode 100644 qpdf/qtest/qpdf/extensions-other.pdf create mode 100644 qpdf/qtest/qpdf/leading-junk.out create mode 100644 qpdf/qtest/qpdf/leading-junk.pdf create mode 100644 qpdf/qtest/qpdf/unfilterable-with-crypt-after.out create mode 100644 qpdf/qtest/qpdf/unfilterable-with-crypt-before.out create mode 100644 qpdf/qtest/qpdf/unfilterable-with-crypt.pdf create mode 100644 qpdf/qtest/qpdf/zero-offset.out create mode 100644 qpdf/qtest/qpdf/zero-offset.pdf diff --git a/ChangeLog b/ChangeLog index fd15bda..71369f9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,146 @@ +2012-12-31 Jay Berkenbilt + + * 4.0.0: release + + * Add new methods qpdf_get_pdf_extension_level, + qpdf_set_r5_encryption_parameters, + qpdf_set_r6_encryption_parameters, + qpdf_set_minimum_pdf_version_and_extension, and + qpdf_force_pdf_version_and_extension to support new functionality + from the C API. + +2012-12-30 Jay Berkenbilt + + * Fix long-standing bug that could theoretically have resulted in + possible misinterpretation of decode parameters in streams. As + far as I can tell, it is extremely unlikely that files with the + characteristics that would have triggered the bug actually exist + in cases that qpdf versions prior to 4.0.0 could have read. + Unencrypted files with encrypted attachments would have triggered + this bug, but qpdf versions prior to 4.0.0 already refused to open + such files. + + * Fix long-standing bug in which a stream that used a crypt + filter and was otherwise not filterable by qpdf would be decrypted + properly but would retain the crypt filter indication in the + file. There are no known ways to create files like this, so it is + unlikely that anyone ever hit this bug. + +2012-12-29 Jay Berkenbilt + + * Add read/write support for both the deprecated Acrobat IX + encryption format and the Acrobat X/PDF 2.0 encryption format + using 256-bit AES keys. Using the Acrobat IX format (R=5) forces + the version of the file to 1.7 with extension level 3. Using the + PDF 2.0 format (R=6) forces it to 1.7 extension level 8. + + * Add new method QPDF::getEncryptionKey to return the actual + encryption key used for encryption of data in the file. The key + is returned as a std::string. + + * Non-compatible API change: change signature of + QPDF::compute_data_key to take the R and V values from the + encryption dictionary. There is no reason for any application + code to call this method since handling of encryption is done + automatically by the qpdf libary. It is used internally by + QPDFWriter. + + * Support reading and decryption of files whose main text is not + encrypted but whose attachments are. More generally, support the + case of files and streams encrypted differently with some + limitations, described in the documentation. This was not + previously supported due to lack of test files, but I created test + files using a trial version of Acrobat XI to fully implement this + case. + + * Incorporate sha2 code from sphlib 3.0. See README for + licensing. Create private pipeline class for computing hashes + with sha256, sha384, and sha512. + + * Allow specification of initialization vector when using AES + filtering. This is required to compute the hash used in /R=6 (PDF + 2.0) encryption. + +2012-12-28 Jay Berkenbilt + + * Add random number generation functions to QUtil. + + * Fix old bug that could cause an infinite loop if user password + recovery methods were called and a password contained the "(" + character (which happens to be the first byte of padding used by + older PDF encryption formats). This bug was noticed while reading + code and would not happen under ordinary usage patterns even if + the password contained that character. + +2012-12-27 Jay Berkenbilt + + * Add awareness of extension level to PDF Version methods for both + reading and writing. This includes adding method + QPDF::getExtensionLevel and new versions of + QPDFWriter::setMinimumPDFVersion and QPDFWriter::forcePDFVersion + that support extension levels. The qpdf command-line tool + interprets version numbers of the form x.y.z as version x.y at + extension level z. + + * Update AES classes to support use of 256-bit keys. + + * Non-compatible API change: Removed public method + QPDF::flattenScalarReferences. Instead, just flatten the scalar + references we actually need to flatten. Flattening scalar + references was a wrong decision years ago and has occasionally + caused other problems, among which were that it caused qpdf to + visit otherwise unreferenced and possibly erroneous objects in the + file when it didn't have to. There's no reason that any + non-internal code would have had to call this. + + * Non-compatible API change: Removed public method + QPDF::decodeStreams which was previously used by qpdf --check but + is no longer used. The decodeStreams method could generate false + positives since it would attempt to access all objects in the file + including those that were not referenced. There's no reason that + any non-internal code would have had to call this. + + * Non-compatible API change: Removed public method + QPDF::trimTrailerForWrite, which was only intended for use by + QPDFWriter and which is no longer used. + +2012-12-26 Jay Berkenbilt + + * Add new fields to QPDF::EncryptionData to support newer + encryption formats (V=5, R=5 and R=6) + + * Non-compatible API change: Change public nested class + QPDF::EncryptionData to make all member fields private and to add + method calls. This is a non-compatible API change, but changing + EncryptionData is necessary to support newer encryption formats, + and making this change will prevent the need from making a + non-compatible change in the future if new fields are added. A + public nested class should never have had public members to begin + with. + +2012-12-25 Jay Berkenbilt + + * Allow PDF header to appear anywhere in the first 1024 bytes of + the file as recommended in the implementation notes of the Adobe + version of the PDF spec. + +2012-11-20 Jay Berkenbilt + + * Add zlib and libpcre to Requires.private in the pkg-config file + to support static linking. Thanks Tobias Hoffmann for pointing + out the omission. + + * Ignore (with warning) non-freed objects in the xref table whose + offset is 0. Some PDF producers (incorrectly) do this. See + https://bugs.linuxfoundation.org/show_bug.cgi?id=1081. + +2012-09-23 Jay Berkenbilt + + * Add public methods QPDF::processInputSource and + QPDFWriter::setOutputPipeline to allow users to read from custom + input sources and to write to custom pipelines. This allows the + maximum flexibility in sources for reading and writing PDF files. + 2012-09-06 Jay Berkenbilt * 3.0.2: release diff --git a/README b/README index 2d82a22..e73d669 100644 --- a/README +++ b/README @@ -2,7 +2,7 @@ This is the QPDF package. Information about it can be found at http://qpdf.sourceforge.net. The source code repository is hosted at github: https://github.com/qpdf/qpdf. -QPDF is copyright (c) 2005-2012 Jay Berkenbilt +QPDF is copyright (c) 2005-2013 Jay Berkenbilt This software may be distributed under the terms of version 2 of the Artistic License which may be found in the source distribution as @@ -48,6 +48,33 @@ obtained from http://www.efgh.com/software/rijndael.htm http://www.efgh.com/software/rijndael.txt +The embedded sha2 code comes from sphlib 3.0 + + http://www.saphir2.com/sphlib/ + +That code has the following license: + + Copyright (c) 2007-2011 Projet RNRT SAPHIR + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + Building on UNIX/Linux ====================== diff --git a/README.maintainer b/README.maintainer index b150eeb..4be45f5 100644 --- a/README.maintainer +++ b/README.maintainer @@ -28,7 +28,7 @@ Release Reminders You can generate these by running valgrind with --gen-suppressions=yes. - * Check all open issues in the sourceforge trackers. + * Check all open issues in the sourceforge trackers and on github. * If any interfaces were added or changed, check C API to see whether changes are appropriate there as well. @@ -58,7 +58,7 @@ Release Reminders done or retargeted. * Each year, update copyright notices. Just search for Copyright. - Last updated: 2012. + Last updated: 2013. * To construct a source distribution from a pristine checkout, make_dist does the following: @@ -91,8 +91,10 @@ Release Reminders libraries. Run ./make_windows_releases from there. You will need to have zip in your path. + * Before releasing, rebuild and test debian package. + * Remember to copy README-what-to-download.txt separately onto the - download area and make it the default for Windows platforms. + download area. * Remember to update the web page including putting new documentation in the "files" subdirectory of the website on sourceforge.net. @@ -104,7 +106,9 @@ Release Reminders git tag -s release-qpdf-$version HEAD -m'qpdf $version' * When releasing on sourceforge, external-libs distributions go in - external-libs/yyyymmdd, and qpdf distributions go in qpdf/vvv + external-libs/yyyymmdd, and qpdf distributions go in qpdf/vvv. + Make the source package the default for all but Windows, and make + the 32-bit mingw build the default for Windows. General Build Stuff diff --git a/TODO b/TODO index ff3ecc8..ec797aa 100644 --- a/TODO +++ b/TODO @@ -1,59 +1,22 @@ General ======= - * See if I can support the encryption format used with /R 5 /V 5, - even though a qpdf-announce subscriber with an adobe.com email - address mentioned that this is deprecated. There is also a new - encryption format coming in a future release, which may be better - to support. As of the qpdf 3.0 release, the specification was not - publicly available yet. + * Improve the random number seed to make it more secure so that we + have stronger random numbers, particularly when multiple files are + generated in the same second. This code may need to be + OS-specific. Probably we should add a method in QUtil to seed with + a strong random number and call this automatically the first time + QUtil::random() is called. * Consider the possibility of doing something locale-aware to support non-ASCII passwords. Update documentation if this is done. - * Look for %PDF header somewhere within the first 1024 bytes of the - file. Also accept headers of the form "%!PS−Adobe−N.n PDF−M.m". - See Implementation notes 13 and 14 in appendix H of the PDF 1.7 - specification. This is bug 3267974. - * Consider impact of article threads on page splitting/merging. Subramanyam provided a test file; see ../misc/article-threads.pdf. Email Q-Count: 431864 from 2009-11-03. Other things to consider: outlines, page labels, thumbnails, zones. There are probably others. - * See whether it's possible to remove the call to - flattenScalarReferences. I can't easily figure out why I do it, - but removing it causes strange test failures in linearization. I - would have to study the optimization and linearization code to - figure out why I added this to begin with and what in the code - assumes it's the case. For enqueueObject and unparseChild in - QPDFWriter, simply removing the checks for indirect scalars seems - sufficient. Looking back at the branch in the apex epub - repository, before flattening scalar references, there was special - case code in QPDFWriter to avoid writing out indirect nulls. It's - still not obvious to me why I did it though. - - To pursue this, remove the call to flattenScalarReferences in - QPDFWriter.cc and disable the logic_error exceptions for indirect - scalars. Just search for flattenScalarReferences in QPDFWriter.cc - since the logic errors have comments that mention - flattenScalarReferences. Then run the test suite. Several files - that explicitly test flattening of scalar references fail, but the - indirect scalars are properly preserved and written. But then - there are some linearized files that have a bunch of unreferenced - objects that contain scalars. Need to figure out what these are - and why they're there. Maybe they're objects that used to be - stream lengths. Probably we just need to make sure don't traverse - through a stream's /Length stream when enqueueing stream - dictionaries. This could potentially happen with any object that - QPDFWriter replaces when writing out files. Such objects would be - orphaned in the newly written file. This could be fixed, but it - may not be worth fixing. - - If flattenScalarReferences is removed, a new method will be needed - for checking PDF files. - * See if we can avoid preserving unreferenced objects in object streams even when preserving the object streams. @@ -95,31 +58,30 @@ Index: QPDFWriter.cc } ------------------------------ - * Handle embedded files. PDF Reference 1.7 section 3.10, "File - Specifications", discusses this. Once we can definitely recognize - all embedded files in a document, we can update the encryption - code to handle it properly. In QPDF_encryption.cc, search for - cf_file. Remove exception thrown if cf_file is different from - cf_stream, and write code in the stream decryption section to use - cf_file instead of cf_stream. In general, add interfaces to get - the list of embedded files and to extract them. To handle general - embedded files associated with the whole document, follow root -> - /Names -> /EmbeddedFiles -> /Names to get to the file specification - dictionaries. Then, in each file specification dictionary, follow - /EF -> /F to the actual stream. There may be other places file - specification dictionaries may appear, and there are also /RF keys - with related files, so reread section 3.10 carefully. + * Provide APIs for embedded files. See *attachments*.pdf in test + suite. The private method findAttachmentStreams finds at least + cases for modern versions of Adobe Reader (>= 1.7, maybe earlier). + PDF Reference 1.7 section 3.10, "File Specifications", discusses + this. + + A sourceforge user asks if qpdf can handle extracting and embedded + resources and references these tools, which may be useful as a + reference. + + http://multivalent.sourceforge.net/Tools/pdf/Extract.html + http://multivalent.sourceforge.net/Tools/pdf/Embed.html * The description of Crypt filters is unclear with respect to how to use them to override /StmF for specific streams. I'm not sure whether qpdf will do the right thing for any specific individual - streams that might have crypt filters. The specification seems to - imply that only embedded file streams and metadata streams can have - crypt filters, and there are already special cases in the code to - 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 - filters. There is an example in the spec of using a crypt filter - on a metadata stream. + streams that might have crypt filters, but I believe it does based + on my testing of a limited subset. The specification seems to imply + that only embedded file streams and metadata streams can have crypt + filters, and there are already special cases in the code to 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 filters. + There is an example in the spec of using a crypt filter on a + metadata stream. For now, we notice /Crypt filters and decode parameters consistent with the example in the PDF specification, and the right thing diff --git a/configure b/configure index 2b33603..0a7280f 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for qpdf 3.0.2. +# Generated by GNU Autoconf 2.69 for qpdf 4.0.0. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -587,8 +587,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='qpdf' PACKAGE_TARNAME='qpdf' -PACKAGE_VERSION='3.0.2' -PACKAGE_STRING='qpdf 3.0.2' +PACKAGE_VERSION='4.0.0' +PACKAGE_STRING='qpdf 4.0.0' PACKAGE_BUGREPORT='' PACKAGE_URL='' @@ -1307,7 +1307,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures qpdf 3.0.2 to adapt to many kinds of systems. +\`configure' configures qpdf 4.0.0 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1372,7 +1372,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of qpdf 3.0.2:";; + short | recursive ) echo "Configuration of qpdf 4.0.0:";; esac cat <<\_ACEOF @@ -1506,7 +1506,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -qpdf configure 3.0.2 +qpdf configure 4.0.0 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2046,7 +2046,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by qpdf $as_me 3.0.2, which was +It was created by qpdf $as_me 4.0.0, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -16390,7 +16390,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by qpdf $as_me 3.0.2, which was +This file was extended by qpdf $as_me 4.0.0, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -16456,7 +16456,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -qpdf config.status 3.0.2 +qpdf config.status 4.0.0 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.ac b/configure.ac index 20a27a9..dff0bd1 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ dnl Process this file with autoconf to produce a configure script. dnl This config.in requires autoconf 2.5 or greater. AC_PREREQ([2.68]) -AC_INIT([qpdf],[3.0.2]) +AC_INIT([qpdf],[4.0.0]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_FILES([autoconf.mk]) diff --git a/doc/qpdf-manual.html b/doc/qpdf-manual.html index 060d6e9..51f005a 100644 --- a/doc/qpdf-manual.html +++ b/doc/qpdf-manual.html @@ -1,6 +1,6 @@ -QPDF Manual

QPDF Manual

For QPDF Version 3.0.2, September 6, 2012

Jay Berkenbilt


General Information

+QPDF Manual

QPDF Manual

For QPDF Version 4.0.0, December 31, 2012

Jay Berkenbilt


General Information

QPDF is a program that does structural, content-preserving transformations on PDF files. QPDF's website is located at http://qpdf.sourceforge.net/. QPDF's source code is hosted on github at https://github.com/qpdf/qpdf. @@ -253,8 +253,8 @@ make empty strings.

The value for - key-length may be 40 - or 128. The restriction flags are dependent upon key length. + key-length may be 40, + 128, or 256. The restriction flags are dependent upon key length. When no additional restrictions are given, the default is to be fully permissive.

@@ -321,6 +321,25 @@ make in testing qpdf itself. This option also forces the PDF version to be at least 1.5.

+ If key-length is 256, + the minimum PDF version is 1.7 with extension level 8, and the + AES-based encryption format used is the PDF 2.0 encryption method + supported by Acrobat X. the same options are available as with + 128 bits with the following exceptions: +

--use-aes

+ This option is not available with 256-bit keys. AES is always + used with 256-bit encryption keys. +

--force-V4

+ This option is not available with 256 keys. +

--force-R5

+ If specified, qpdf sets the minimum version to 1.7 at + extension level 3 and writes the deprecated encryption format + used by Acrobat version IX. This option should not be used in + practice to generate PDF files that will be in general use, + but it can be useful to generate files if you are trying to + test proper support in another application for PDF files + encrypted in this way. +

The default for each permission option is to be fully permissive.

3.4. Page Selection Options

Starting with qpdf 3.0, it is possible to split and merge PDF @@ -471,17 +490,31 @@ outfile.pdf used. It is seldom necessary to use this option since qpdf will automatically increase the version as needed when adding features that require newer PDF readers. +

+ The version number may be expressed in the form + major.minor.extension-level, in + which case the version is interpreted as + major.minor at extension level + extension-level. For example, + version 1.7.8 represents version 1.7 at + extension level 8. Note that minimal syntax checking is done + on the command line.

--force-version=version

This option forces the PDF version to be the exact version specified even when the file may have content that - is not supported in that version. In some cases, - forcing the output file's PDF version to be lower than that of - the input file will cause qpdf to disable certain features of - the document. Specifically, AES encryption is disabled if the - version is less than 1.6, cleartext metadata and object - streams are disabled if less than 1.5, 128-bit encryption keys - are disabled if less than 1.4, and all encryption is disabled - if less than 1.3. Even with these precautions, qpdf won't be + is not supported in that version. The version + number is interpreted in the same way as with + --min-version so that extension levels can be + set. In some cases, forcing the output file's PDF version to + be lower than that of the input file will cause qpdf to + disable certain features of the document. Specifically, + 256-bit keys are disabled if the version is less than 1.7 with + extension level 8 (except R5 is disabled if less than 1.7 with + extension level 3), AES encryption is disabled if the version + is less than 1.6, cleartext metadata and object streams are + disabled if less than 1.5, 128-bit encryption keys are + disabled if less than 1.4, and all encryption is disabled if + less than 1.3. Even with these precautions, qpdf won't be able to do things like eliminate use of newer image compression schemes, transparency groups, or other features that may have been added in more recent versions of PDF. @@ -927,7 +960,7 @@ outfile.pdf password-protected files. QPDF does not enforce encryption parameters and will treat user and owner passwords equivalently. Either password may be used to access an encrypted file. - [1] + [1] QPDF will allow recovery of a user password given an owner password. The input PDF file must be seekable. (Output files written by QPDFWriter need @@ -1037,6 +1070,29 @@ outfile.pdf strings given an encryption key. This is used by QPDFWriter when it rewrites encrypted files. +

+ When copying encrypted files, unless otherwise directed, qpdf will + preserve any encryption in force in the original file. qpdf can + do this with either the user or the owner password. There is no + difference in capability based on which password is used. When 40 + or 128 bit encryption keys are used, the user password can be + recovered with the owner password. With 256 keys, the user and + owner passwords are used independently to encrypt the actual + encryption key, so while either can be used, the owner password + can no longer be used to recover the user password. +

+ Starting with version 4.0.0, qpdf can read files that are not + encrypted but that contain encrypted attachments, but it cannot + write such files. qpdf also requires the password to be specified + in order to open the file, not just to extract attachments, since + once the file is open, all decryption is handled transparently. + When copying files like this while preserving encryption, qpdf + will apply the file's encryption to everything in the file, not + just to the attachments. When decrypting the file, qpdf will + decrypt the attachments. In general, when copying PDF files with + multiple encryption formats, qpdf will choose the newest format. + The only exception to this is that clear-text metadata will be + preserved as clear-text if it is that way in the original file.

6.4. Adding and Removing Pages

While qpdf's API has supported adding and modifying objects for some time, version 3.0 introduces specific methods for adding and @@ -1222,7 +1278,7 @@ outfile.pdf filter should write to whatever type of output is required. The QPDF class has an interface to write raw or filtered stream contents to a given pipeline. -



[1] +



[1] As pointed out earlier, the intention is not for qpdf to be used to bypass security on files. but as any open source PDF consumer may be easily modified to bypass basic PDF document security, @@ -1585,7 +1641,130 @@ print "\n";

Appendix A. Release Notes

For a detailed list of changes, please see the file ChangeLog in the source distribution. -

3.0.2: September 6, 2012
  • +

    4.0.0: December 31, 2012
    • + Major enhancement: support has been added for newer encryption + schemes supported by version X of Adobe Acrobat. This + includes use of 127-character passwords, 256-bit encryption + keys, and the encryption scheme specified in ISO 32000-2, the + PDF 2.0 specification. This scheme can be chosen from the + command line by specifying use of 256-bit keys. qpdf also + supports the deprecated encryption method used by Acrobat IX. + This encryption style has known security weaknesses and should + not be used in practice. However, such files exist “in + the wild,” so support for this scheme is still useful. + New methods + QPDFWriter::setR6EncryptionParameters + (for the PDF 2.0 scheme) and + QPDFWriter::setR5EncryptionParameters + (for the deprecated scheme) have been added to enable these + new encryption schemes. Corresponding functions have been + added to the C API as well. +

    • + Full support for Adobe extension levels in PDF version + information. Starting with PDF version 1.7, corresponding to + ISO 32000, Adobe adds new functionality by increasing the + extension level rather than increasing the version. This + support includes addition of the + QPDF::getExtensionLevel method for + retrieving the document's extension level, addition of + versions of + QPDFWriter::setMinimumPDFVersion and + QPDFWriter::forcePDFVersion that accept + an extension level, and extended syntax for specifying forced + and minimum versions on the command line as described in Section 3.5, “Advanced Transformation Options”. Corresponding + functions have been added to the C API as well. +

    • + Minor fixes to prevent qpdf from referencing objects in the + file that are not referenced in the file's overall structure. + Most files don't have any such objects, but some files have + contain unreferenced objects with errors, so these fixes + prevent qpdf from needlessly rejecting or complaining about + such objects. +

    • + Add new generalized methods for reading and writing files + from/to programmer-defined sources. The method + QPDF::processInputSource allows the + programmer to use any input source for the input file, and + QPDFWriter::setOutputPipeline allows the + programmer to write the output file through any pipeline. + These methods would make it possible to perform any number of + specialized operations, such as accessing external storage + systems, creating bindings for qpdf in other programming + languages that have their own I/O systems, etc. +

    • + Add new method QPDF::getEncryptionKey for + retrieving the underlying encryption key used in the file. +

    • + This release includes a small handful of non-compatible API + changes. While effort is made to avoid such changes, all the + non-compatible API changes in this version were to parts of + the API that would likely never be used outside the library + itself. In all cases, the altered methods or structures were + parts of the QPDF that were public to + enable them to be called from either + QPDFWriter or were part of validation + code that was over-zealous in reporting problems in parts of + the file that would not ordinarily be referenced. In no case + did any of the removed methods do anything worse that falsely + report error conditions in files that were broken in ways that + didn't matter. The following public parts of the + QPDF class were changed in a + non-compatible way: +

      • + Updated nested QPDF::EncryptionData + class to add fields needed by the newer encryption formats, + member variables changed to private so that future changes + will not require breaking backward compatibility. +

      • + Added additional parameters to + compute_data_key, which is used by + QPDFWriter to compute the encryption + key used to encrypt a specific object. +

      • + Removed the method + flattenScalarReferences. This method + was previously used prior to writing a new PDF file, but it + has the undesired side effect of causing qpdf to read + objects in the file that were not referenced. Some + otherwise files have unreferenced objects with errors in + them, so this could cause qpdf to reject files that would + be accepted by virtually all other PDF readers. In fact, + qpdf relied on only a very small part of what + flattenScalarReferences did, so only this part has been + preserved, and it is now done directly inside + QPDFWriter. +

      • + Removed the method decodeStreams. + This method was used by the --check option + of the qpdf command-line tool to force + all streams in the file to be decoded, but it also suffered + from the problem of opening otherwise unreferenced streams + and thus could report false positive. The + --check option now causes qpdf to go + through all the motions of writing a new file based on the + original one, so it will always reference and check exactly + those parts of a file that any ordinary viewer would check. +

      • + Removed the method + trimTrailerForWrite. This method was + used by QPDFWriter to modify the + original QPDF object by removing fields from the trailer + dictionary that wouldn't apply to the newly written file. + This functionality, though generally harmless, was a poor + implementation and has been replaced by having QPDFWriter + filter these out when copying the trailer rather than + modifying the original QPDF object. (Note that qpdf never + modifies the original file itself.) +

      +

    • + Allow the PDF header to appear anywhere in the first 1024 + bytes of the file. This is consistent with what other readers + do. +

    • + Fix the pkg-config files to list zlib and + pcre in Requires.private to better + support static linking using pkg-config. +

    3.0.2: September 6, 2012
    • Bug fix: QPDFWriter::setOutputMemory did not work when not used with QPDFWriter::setStaticID, which made it @@ -2047,4 +2226,12 @@ print "\n"; previously been smaller types. This change was required to support files larger than two gigabytes in size.

    +

    Appendix D. Upgrading to 4.0

    + While version 4.0 includes a few non-compatible API changes, it is + very unlikely that anyone's code would have used any of those parts + of the API since they generally required information that would + only be available inside the library. In the unlikely event that + you should run into trouble, please see the ChangeLog. See also + Appendix A, Release Notes for a complete list of the + non-compatible API changes made in this version.

diff --git a/doc/qpdf-manual.pdf b/doc/qpdf-manual.pdf index 75f62234d6a23b0825af3957ccb191e6c4ff5208..91323bf3c324bab6cb045a1cf64f5ca9e1cb28a4 100644 GIT binary patch delta 89513 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

vElsT71sXbqIw7!rpeP^bBI9cvx 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 S@)8zTSZ$QXk8ioFwe&v~>W$q1 diff --git a/include/qpdf/Buffer.hh b/include/qpdf/Buffer.hh index 7fabe9a..22e1f34 100644 --- a/include/qpdf/Buffer.hh +++ b/include/qpdf/Buffer.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/BufferInputSource.hh b/include/qpdf/BufferInputSource.hh index 48f6e3a..222f71e 100644 --- a/include/qpdf/BufferInputSource.hh +++ b/include/qpdf/BufferInputSource.hh @@ -1,3 +1,11 @@ +/* Copyright (c) 2005-2013 Jay Berkenbilt + * + * This file is part of qpdf. This software may be distributed under + * the terms of version 2 of the Artistic License which may be found + * in the source distribution. It is provided "as is" without express + * or implied warranty. + */ + #ifndef __QPDF_BUFFERINPUTSOURCE_HH__ #define __QPDF_BUFFERINPUTSOURCE_HH__ diff --git a/include/qpdf/Constants.h b/include/qpdf/Constants.h index 176392e..6d34020 100644 --- a/include/qpdf/Constants.h +++ b/include/qpdf/Constants.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005-2012 Jay Berkenbilt +/* Copyright (c) 2005-2013 Jay Berkenbilt * * This file is part of qpdf. This software may be distributed under * the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/DLL.h b/include/qpdf/DLL.h index 640e4f6..c2c85dc 100644 --- a/include/qpdf/DLL.h +++ b/include/qpdf/DLL.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005-2012 Jay Berkenbilt +/* Copyright (c) 2005-2013 Jay Berkenbilt * * This file is part of qpdf. This software may be distributed under * the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/FileInputSource.hh b/include/qpdf/FileInputSource.hh index 6129326..001af17 100644 --- a/include/qpdf/FileInputSource.hh +++ b/include/qpdf/FileInputSource.hh @@ -1,3 +1,11 @@ +/* Copyright (c) 2005-2013 Jay Berkenbilt + * + * This file is part of qpdf. This software may be distributed under + * the terms of version 2 of the Artistic License which may be found + * in the source distribution. It is provided "as is" without express + * or implied warranty. + */ + #ifndef __QPDF_FILEINPUTSOURCE_HH__ #define __QPDF_FILEINPUTSOURCE_HH__ diff --git a/include/qpdf/InputSource.hh b/include/qpdf/InputSource.hh index 782d888..510daac 100644 --- a/include/qpdf/InputSource.hh +++ b/include/qpdf/InputSource.hh @@ -1,3 +1,11 @@ +/* Copyright (c) 2005-2013 Jay Berkenbilt + * + * This file is part of qpdf. This software may be distributed under + * the terms of version 2 of the Artistic License which may be found + * in the source distribution. It is provided "as is" without express + * or implied warranty. + */ + #ifndef __QPDF_INPUTSOURCE_HH__ #define __QPDF_INPUTSOURCE_HH__ diff --git a/include/qpdf/Pipeline.hh b/include/qpdf/Pipeline.hh index 2de17ad..aa86003 100644 --- a/include/qpdf/Pipeline.hh +++ b/include/qpdf/Pipeline.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found @@ -44,7 +44,13 @@ class Pipeline // Subclasses should implement write and finish to do their jobs // and then, if they are not end-of-line pipelines, call - // getNext()->write or getNext()->finish. + // getNext()->write or getNext()->finish. It would be really nice + // if write could take unsigned char const*, but this would make + // it much more difficult to write pipelines around legacy + // interfaces whose calls don't want pointers to const data. As a + // rule, pipelines should generally not be modifying the data + // passed to them. They should, instead, create new data to pass + // downstream. QPDF_DLL virtual void write(unsigned char* data, size_t len) = 0; QPDF_DLL diff --git a/include/qpdf/Pl_Buffer.hh b/include/qpdf/Pl_Buffer.hh index 57473fd..3019181 100644 --- a/include/qpdf/Pl_Buffer.hh +++ b/include/qpdf/Pl_Buffer.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/Pl_Concatenate.hh b/include/qpdf/Pl_Concatenate.hh index e8fd64e..568eb04 100644 --- a/include/qpdf/Pl_Concatenate.hh +++ b/include/qpdf/Pl_Concatenate.hh @@ -1,3 +1,11 @@ +/* Copyright (c) 2005-2013 Jay Berkenbilt + * + * This file is part of qpdf. This software may be distributed under + * the terms of version 2 of the Artistic License which may be found + * in the source distribution. It is provided "as is" without express + * or implied warranty. + */ + #ifndef __PL_CONCATENATE_HH__ #define __PL_CONCATENATE_HH__ diff --git a/include/qpdf/Pl_Count.hh b/include/qpdf/Pl_Count.hh index 7c5a418..d44e688 100644 --- a/include/qpdf/Pl_Count.hh +++ b/include/qpdf/Pl_Count.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/Pl_Discard.hh b/include/qpdf/Pl_Discard.hh index 79b8564..3110b45 100644 --- a/include/qpdf/Pl_Discard.hh +++ b/include/qpdf/Pl_Discard.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/Pl_Flate.hh b/include/qpdf/Pl_Flate.hh index 31e3de3..188a79b 100644 --- a/include/qpdf/Pl_Flate.hh +++ b/include/qpdf/Pl_Flate.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/Pl_StdioFile.hh b/include/qpdf/Pl_StdioFile.hh index 0a0f15d..f91e572 100644 --- a/include/qpdf/Pl_StdioFile.hh +++ b/include/qpdf/Pl_StdioFile.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/PointerHolder.hh b/include/qpdf/PointerHolder.hh index a2f33e9..a1f756b 100644 --- a/include/qpdf/PointerHolder.hh +++ b/include/qpdf/PointerHolder.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/QPDF.hh b/include/qpdf/QPDF.hh index e594a44..d436c37 100644 --- a/include/qpdf/QPDF.hh +++ b/include/qpdf/QPDF.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found @@ -72,6 +72,13 @@ class QPDF char const* buf, size_t length, char const* password = 0); + // Parse a PDF file loaded from a custom InputSource. If you have + // your own method of retrieving a PDF file, you can subclass + // InputSource and use this method. + QPDF_DLL + void processInputSource(PointerHolder, + char const* password = 0); + // Create a QPDF object for an empty PDF. This PDF has no pages // or objects other than a minimal trailer, a document catalog, // and a /Pages tree containing zero pages. Pages and other @@ -137,6 +144,8 @@ class QPDF QPDF_DLL std::string getPDFVersion() const; QPDF_DLL + int getExtensionLevel(); + QPDF_DLL QPDFObjectHandle getTrailer(); QPDF_DLL QPDFObjectHandle getRoot(); @@ -215,12 +224,16 @@ class QPDF // Encryption support - enum encryption_method_e { e_none, e_unknown, e_rc4, e_aes }; - struct EncryptionData + enum encryption_method_e { e_none, e_unknown, e_rc4, e_aes, e_aesv3 }; + class EncryptionData { + public: + // This class holds data read from the encryption dictionary. EncryptionData(int V, int R, int Length_bytes, int P, std::string const& O, std::string const& U, + std::string const& OE, std::string const& UE, + std::string const& Perms, std::string const& id1, bool encrypt_metadata) : V(V), R(R), @@ -228,17 +241,47 @@ class QPDF P(P), O(O), U(U), + OE(OE), + UE(UE), + Perms(Perms), id1(id1), encrypt_metadata(encrypt_metadata) { } + int getV() const; + int getR() const; + int getLengthBytes() const; + int getP() const; + std::string const& getO() const; + std::string const& getU() const; + std::string const& getOE() const; + std::string const& getUE() const; + std::string const& getPerms() const; + std::string const& getId1() const; + bool getEncryptMetadata() const; + + void setO(std::string const&); + void setU(std::string const&); + void setV5EncryptionParameters(std::string const& O, + std::string const& OE, + std::string const& U, + std::string const& UE, + std::string const& Perms); + + private: + EncryptionData(EncryptionData const&); + EncryptionData& operator=(EncryptionData const&); + int V; int R; int Length_bytes; int P; std::string O; std::string U; + std::string OE; + std::string UE; + std::string Perms; std::string id1; bool encrypt_metadata; }; @@ -283,7 +326,7 @@ class QPDF QPDF_DLL static std::string compute_data_key( std::string const& encryption_key, int objid, int generation, - bool use_aes); + bool use_aes, int encryption_V, int encryption_R); QPDF_DLL static std::string compute_encryption_key( std::string const& password, EncryptionData const& data); @@ -294,6 +337,14 @@ class QPDF int V, int R, int key_len, int P, bool encrypt_metadata, std::string const& id1, std::string& O, std::string& U); + QPDF_DLL + static void compute_encryption_parameters_V5( + char const* user_password, char const* owner_password, + int V, int R, int key_len, int P, bool encrypt_metadata, + std::string const& id1, + std::string& encryption_key, + std::string& O, std::string& U, + std::string& OE, std::string& UE, std::string& Perms); // Return the full user password as stored in the PDF file. If // you are attempting to recover the user password in a // user-presentable form, call getTrimmedUserPassword() instead. @@ -302,6 +353,10 @@ class QPDF // Return human-readable form of user password. QPDF_DLL std::string getTrimmedUserPassword() const; + // Return the previously computed or retrieved encryption key for + // this file + QPDF_DLL + std::string getEncryptionKey() const; // Linearization support @@ -345,24 +400,8 @@ class QPDF void optimize(std::map const& object_stream_data, bool allow_changes = true); - // Replace all references to indirect objects that are "scalars" - // (i.e., things that don't have children: not arrays, streams, or - // dictionaries) with direct objects. - QPDF_DLL - void flattenScalarReferences(); - - // Decode all streams, discarding the output. Used to check - // correctness of stream encoding. - QPDF_DLL - void decodeStreams(); - // For QPDFWriter: - // Remove /ID, /Encrypt, and /Prev keys from the trailer - // dictionary since these are regenerated during write. - QPDF_DLL - void trimTrailerForWrite(); - // Get lists of all objects in order according to the part of a // linearized file that they belong to. QPDF_DLL @@ -577,6 +616,7 @@ class QPDF int& act_objid, int& act_generation); PointerHolder resolve(int objid, int generation); void resolveObjectsInStream(int obj_stream_number); + void findAttachmentStreams(); // Calls finish() on the pipeline when done but does not delete it void pipeStreamData(int objid, int generation, @@ -600,6 +640,13 @@ class QPDF void initializeEncryption(); std::string getKeyForObject(int objid, int generation, bool use_aes); void decryptString(std::string&, int objid, int generation); + static std::string compute_encryption_key_from_password( + std::string const& password, EncryptionData const& data); + static std::string recover_encryption_key_with_password( + std::string const& password, EncryptionData const& data); + static std::string recover_encryption_key_with_password( + std::string const& password, EncryptionData const& data, + bool& perms_valid); void decryptStream( Pipeline*& pipeline, int objid, int generation, QPDFObjectHandle& stream_dict, @@ -953,6 +1000,7 @@ class QPDF std::ostream* err_stream; bool attempt_recovery; int encryption_V; + int encryption_R; bool encrypt_metadata; std::map crypt_filters; encryption_method_e cf_stream; @@ -977,6 +1025,7 @@ class QPDF PointerHolder copied_streams; // copied_stream_data_provider is owned by copied_streams CopiedStreamDataProvider* copied_stream_data_provider; + std::set attachment_streams; // Linearization data qpdf_offset_t first_xref_item_offset; // actual value from file diff --git a/include/qpdf/QPDFExc.hh b/include/qpdf/QPDFExc.hh index 30d576e..c0a0598 100644 --- a/include/qpdf/QPDFExc.hh +++ b/include/qpdf/QPDFExc.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/QPDFObject.hh b/include/qpdf/QPDFObject.hh index 4626eb6..8bb6102 100644 --- a/include/qpdf/QPDFObject.hh +++ b/include/qpdf/QPDFObject.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/QPDFObjectHandle.hh b/include/qpdf/QPDFObjectHandle.hh index 0375b83..75912e7 100644 --- a/include/qpdf/QPDFObjectHandle.hh +++ b/include/qpdf/QPDFObjectHandle.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/QPDFTokenizer.hh b/include/qpdf/QPDFTokenizer.hh index 6b385b4..1835fcb 100644 --- a/include/qpdf/QPDFTokenizer.hh +++ b/include/qpdf/QPDFTokenizer.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/QPDFWriter.hh b/include/qpdf/QPDFWriter.hh index 2c1c32f..e8b744d 100644 --- a/include/qpdf/QPDFWriter.hh +++ b/include/qpdf/QPDFWriter.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found @@ -95,6 +95,15 @@ class QPDFWriter QPDF_DLL Buffer* getBuffer(); + // Supply your own pipeline object. Output will be written to + // this pipeline, and QPDFWriter will call finish() on the + // pipeline. It is the caller's responsibility to manage the + // memory for the pipeline. The pipeline is never deleted by + // QPDFWriter, which makes it possible for you to call additional + // methods on the pipeline after the writing is finished. + QPDF_DLL + void setOutputPipeline(Pipeline*); + // Setting Parameters // Set the value of object stream mode. In disable mode, we never @@ -144,6 +153,8 @@ class QPDFWriter // streams are used. QPDF_DLL void setMinimumPDFVersion(std::string const&); + QPDF_DLL + void setMinimumPDFVersion(std::string const&, int extension_level); // Force the PDF version of the output file to be a given version. // Use of this function may create PDF files that will not work @@ -162,6 +173,8 @@ class QPDFWriter // object streams. QPDF_DLL void forcePDFVersion(std::string const&); + QPDF_DLL + void forcePDFVersion(std::string const&, int extension_level); // Provide additional text to insert in the PDF file somewhere // near the beginning of the file. This can be used to add @@ -210,8 +223,9 @@ class QPDFWriter // content normalization. Note that setting R2 encryption // parameters sets the PDF version to at least 1.3, setting R3 // encryption parameters pushes the PDF version number to at least - // 1.4, and setting R4 parameters pushes the version to at least - // 1.5, or if AES is used, 1.6. + // 1.4, setting R4 parameters pushes the version to at least 1.5, + // or if AES is used, 1.6, and setting R5 or R6 parameters pushes + // the version to at least 1.7 with extension level 3. QPDF_DLL void setR2EncryptionParameters( char const* user_password, char const* owner_password, @@ -228,6 +242,21 @@ class QPDFWriter bool allow_accessibility, bool allow_extract, qpdf_r3_print_e print, qpdf_r3_modify_e modify, bool encrypt_metadata, bool use_aes); + // R5 is deprecated. Do not use it for production use. Writing + // R5 is supported by qpdf primarily to generate test files for + // applications that may need to test R5 support. + QPDF_DLL + void setR5EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + bool encrypt_metadata); + QPDF_DLL + void setR6EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + bool encrypt_metadata_aes); // Create linearized output. Disables qdf mode, content // normalization, and stream prefiltering. @@ -277,7 +306,8 @@ class QPDFWriter char const* user_password, char const* owner_password, bool allow_accessibility, bool allow_extract, qpdf_r3_print_e print, qpdf_r3_modify_e modify); - void disableIncompatibleEncryption(int major, int minor); + void disableIncompatibleEncryption(int major, int minor, + int extension_level); void parseVersion(std::string const& version, int& major, int& minor) const; int compareVersions(int major1, int minor1, int major2, int minor2) const; void setEncryptionParameters( @@ -286,10 +316,14 @@ class QPDFWriter void setEncryptionParametersInternal( int V, int R, int key_len, long P, std::string const& O, std::string const& U, - std::string const& id1, std::string const& user_password); + std::string const& OE, std::string const& UE, std::string const& Perms, + std::string const& id1, std::string const& user_password, + std::string const& encryption_key); void setDataKey(int objid); int openObject(int objid = 0); void closeObject(int objid); + QPDFObjectHandle getTrimmedTrailer(); + void prepareFileForWrite(); void writeStandard(); void writeLinearized(); void enqueuePart(std::vector& part); @@ -361,11 +395,17 @@ class QPDFWriter bool encrypt_metadata; bool encrypt_use_aes; std::map encryption_dictionary; + int encryption_V; + int encryption_R; std::string id1; // for /ID key of std::string id2; // trailer dictionary + std::string final_pdf_version; + int final_extension_level; std::string min_pdf_version; + int min_extension_level; std::string forced_pdf_version; + int forced_extension_level; std::string extra_header_text; int encryption_dict_objid; std::string cur_data_key; diff --git a/include/qpdf/QPDFXRefEntry.hh b/include/qpdf/QPDFXRefEntry.hh index f27b434..93dac02 100644 --- a/include/qpdf/QPDFXRefEntry.hh +++ b/include/qpdf/QPDFXRefEntry.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/QTC.hh b/include/qpdf/QTC.hh index 1433742..c8ee6ad 100644 --- a/include/qpdf/QTC.hh +++ b/include/qpdf/QTC.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found diff --git a/include/qpdf/QUtil.hh b/include/qpdf/QUtil.hh index cdeefd7..ddba97d 100644 --- a/include/qpdf/QUtil.hh +++ b/include/qpdf/QUtil.hh @@ -1,4 +1,4 @@ -// Copyright (c) 2005-2012 Jay Berkenbilt +// Copyright (c) 2005-2013 Jay Berkenbilt // // This file is part of qpdf. This software may be distributed under // the terms of version 2 of the Artistic License which may be found @@ -83,6 +83,18 @@ namespace QUtil // encoding for the unicode value passed in. QPDF_DLL std::string toUTF8(unsigned long uval); + + // Wrapper around random from stdlib. Calls srandom automatically + // the first time it is called. + QPDF_DLL + long random(); + + // Wrapper around srandom from stdlib. + QPDF_DLL + void srandom(unsigned int seed); + + QPDF_DLL + void initializeWithRandomBytes(unsigned char* data, size_t len); }; #endif // __QUTIL_HH__ diff --git a/include/qpdf/Types.h b/include/qpdf/Types.h index 0d6b8a2..146acc2 100644 --- a/include/qpdf/Types.h +++ b/include/qpdf/Types.h @@ -1,3 +1,11 @@ +/* Copyright (c) 2005-2013 Jay Berkenbilt + * + * This file is part of qpdf. This software may be distributed under + * the terms of version 2 of the Artistic License which may be found + * in the source distribution. It is provided "as is" without express + * or implied warranty. + */ + #ifndef __QPDFTYPES_H__ #define __QPDFTYPES_H__ diff --git a/include/qpdf/qpdf-c.h b/include/qpdf/qpdf-c.h index beba231..ee4fa58 100644 --- a/include/qpdf/qpdf-c.h +++ b/include/qpdf/qpdf-c.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2005-2012 Jay Berkenbilt +/* Copyright (c) 2005-2013 Jay Berkenbilt * * This file is part of qpdf. This software may be distributed under * the terms of version 2 of the Artistic License which may be found @@ -213,6 +213,10 @@ extern "C" { QPDF_DLL char const* qpdf_get_pdf_version(qpdf_data qpdf); + /* Return the extension level of the PDF file. */ + QPDF_DLL + int qpdf_get_pdf_extension_level(qpdf_data qpdf); + /* Return the user password. If the file is opened using the * owner password, the user password may be retrieved using this * function. If the file is opened using the user password, this @@ -359,14 +363,36 @@ extern "C" { QPDF_BOOL encrypt_metadata, QPDF_BOOL use_aes); QPDF_DLL + void qpdf_set_r5_encryption_parameters( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + enum qpdf_r3_print_e print, enum qpdf_r3_modify_e modify, + QPDF_BOOL encrypt_metadata); + + QPDF_DLL + void qpdf_set_r6_encryption_parameters( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + enum qpdf_r3_print_e print, enum qpdf_r3_modify_e modify, + QPDF_BOOL encrypt_metadata); + + QPDF_DLL void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value); QPDF_DLL void qpdf_set_minimum_pdf_version(qpdf_data qpdf, char const* version); QPDF_DLL + void qpdf_set_minimum_pdf_version_and_extension( + qpdf_data qpdf, char const* version, int extension_level); + + QPDF_DLL void qpdf_force_pdf_version(qpdf_data qpdf, char const* version); + QPDF_DLL + void qpdf_force_pdf_version_and_extension( + qpdf_data qpdf, char const* version, int extension_level); + /* Do actual write operation. */ QPDF_DLL QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf); diff --git a/ispell-words b/ispell-words index 51f70a1..0029212 100644 --- a/ispell-words +++ b/ispell-words @@ -1,9 +1,15 @@ +aa aaa ab +abacc abc ABCD +abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnom +abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnom abcde abcdefABCDEF +abcdefghbcdefghicdefghijdefghijkefghijklfghijklmg +abcdefghbcdefghicdefghijdefghijkefghijklfghijklmg abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi abÏ @@ -17,6 +23,7 @@ acroread activatePipelineStack ActiveState acyclic +adbe addPage addPageAt addPageContents @@ -24,8 +31,12 @@ addToTable adjustAESStreamLength admon Adobeâ +ae +aeadb aes AESv +aesv +afa ageneration AHx AIX @@ -46,6 +57,7 @@ aobjid apexcovantage API APIs +appendable appendItem appendString arg @@ -85,16 +97,24 @@ autoheader autolabel automake autotools +ba backref backreference backrefs +badBits badLength BADMAGIC BADOPTION +baeca BaseFont basename +BaseVersion +bb +bc +beb Berkenbilt betweenTokens +bf binmode BitsPerComponent bitstream @@ -110,12 +130,14 @@ BT buf BufferInputSource buflen +bufp bufpl bufsize BUGREPORT buildrules bw bytesNeeded +ca calculateHOutline calculateHPageOffset calculateHSharedObject @@ -123,16 +145,22 @@ calculateLinearizationData calculateXrefStreamPadding callHello CAPTURECOUNT +cb cbc cc ccase +ccc CCF CCITTFaxDecode cd +cdc cdict ce +cec +ced cerr cf +cfea CFM ch ChangeLog @@ -161,6 +189,7 @@ clearPipelineStack cleartext closeObject cmd +codepage codepoint ColorSpace com @@ -192,17 +221,26 @@ CryptFilterDecodeParms cso csoe css +cstdio cstr cstring ctest +ctx ctype cygwin +da daae +dae +db +dc DCT DCTDecode +dd +ddaf ddd de debian +dec declspec DecodeParms decodeRow @@ -233,6 +271,7 @@ DIR dirname disableCBC disableIncompatibleEncryption +disablePadding dist distclean dlfcn @@ -257,8 +296,17 @@ dumpLinearizationDataInternal dwHighDateTime dwLowDateTime DWORD +ea +eadb earlychange EarlyChange +eb +ebae +ecc +ecedd +eded +eeb +eeee EF EFF efgh @@ -266,6 +314,7 @@ EI elif elt EmbeddedFiles +embeddedFiles emptyPDF en encodeDataIncrementally @@ -303,14 +352,18 @@ esize exc exe exp +ExtensionLevel extern fb fBqpdf +fc +fcc fclose fcntl fd +feebbd ferror -FF +ff ffff fflush fghij @@ -322,10 +375,12 @@ FileInputSource fileno filenow filep +Filespec FILETIME filetrailer filterCompressedObjects findAndSkipNextEOL +findAttachmentStreams findPage fIoptions fIoutfilename @@ -354,7 +409,9 @@ fullinfo fullpad func fwrite +Gagic GajiÄ +Gajic gcc gen generateHintStream @@ -377,16 +434,20 @@ getCount getDataChecksum getDict getDictAsMap +getEncryptionKey +getEncryptMetadata getenv GetEnvironmentVariable getErrorCode getErrorMessage +getExtensionLevel getFileChecksum getFilename getFilePosition getFirstChar getGeneration getHexDigest +getId getIntValue getItem getKey @@ -395,6 +456,7 @@ getKeys getLastChar getLastOffset getLength +getLengthBytes getLinearizationOffset getLinearizedParts getMatch @@ -403,20 +465,26 @@ getName getNext getNItems getNumericValue +getO getObject getObjectByID getObjectID getObjectStreamData getObjStreamIndex getObjStreamNumber +getOE getOffset getOffsetLength getOwningQPDF +getP getPaddedUserPassword getPageContents getPageImages getPDFVersion +getPerms getPointer +getR +getRawDigest getRawStreamData getRawValue getRealValue @@ -428,13 +496,16 @@ getStringValue GetSystemTime getToken getTrailer +getTrimmedTrailer getTrimmedUserPassword getTrimmedUserPassword's getType -GETU +getU +getUE getUncompressedObject getUserPassword getUTF +getV getVal getValue getWarnings @@ -466,6 +537,8 @@ hexstrings HGeneric hh HighPart +hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstn +hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstn hlen Hoffmann HOi @@ -511,6 +584,7 @@ initializeEncryption initializePipelineStack initializeSpecialStreams initializeVector +initializeWithRandomBytes inline inode inputLen @@ -655,6 +729,7 @@ msvc MSVC's msys multibyte +multiline multithreaded Mutator mutators @@ -723,6 +798,8 @@ ObjStm ObjUser objusers oc +OE +OffsetInputSource og ogs oiter @@ -743,11 +820,13 @@ ostream ostringstream OtherPage ou +OUE ous outbuf OUTDOC outfile outfilename +outlength outputLengthNextN ovecsize ovector @@ -760,6 +839,7 @@ pageobj PageSpec para param +params parms parseInternal parseVersion @@ -768,12 +848,15 @@ pathsep Pavlyuk pb pbytes +pc pcre pcreapi pdf PDFâ +PDFContext +PDFDocEncoding pdfDumpInfoDict -PDFS +PDFs pdlin pe perl @@ -783,6 +866,7 @@ php pipeStreamData pipeStringAndFinish Pkey +pkg PKI pl plaintext @@ -798,6 +882,7 @@ pre precompiled prefilering prefiltering +prepareFileForWrite presentCharacter presentEOF preserveObjectStreams @@ -806,6 +891,7 @@ printability printf processChar processFile +processInputSource processMemoryFile processRow processXRefStream @@ -814,6 +900,7 @@ ProcSet procsets programlisting provideStreamData +proxied PSâ pt pthread @@ -905,6 +992,8 @@ replaceOrRemoveKey replaceReserved replaceStreamData reserveObjects +resetBits +resolveLiteral resolveObjectsInStream retargeted retested @@ -929,6 +1018,7 @@ RStream RunLengthDecode runtest sAlT +SASLprep se sed seekable @@ -943,22 +1033,26 @@ setContentNormalization setDataKey setEncryptionParameters setEncryptionParametersInternal +setExtraHeaderText setFile setFilename setFromVector setIgnoreXRefStreams setItem +setIV setLastObjectDescription setLastOffset setLinearization setLineBuf setMinimumPDFVersion setmode +setO setObjectStreamMode setObjGen setOutputFile setOutputFilename setOutputMemory +setOutputPipeline setOutputStreams setPreserveEncryption setQDFMode @@ -969,7 +1063,10 @@ setStreamDataMode setSuppressOriginalObjectIDs setSuppressWarnings setTrailer +setU +setV setvbuf +sha shallowCopy showLinearizationData showXRefTable @@ -978,6 +1075,7 @@ skipToNextByte soe sourceforge SourceForge +sph sprintf srand srandom @@ -1006,6 +1104,7 @@ StreamDataProvider strerror StrF StringDecrypter +stringprep stripesize strlen strncmp @@ -1064,6 +1163,7 @@ tt txt uc udata +UE uinow uint uiter @@ -1076,6 +1176,7 @@ uncompresesd uncompress uncompressing undef +understandDecodeParams unencrypted unfilterable ungetc @@ -1100,6 +1201,7 @@ updatePagesCache url UseOutlines useStaticIV +useZeroIV USLetter usr utf @@ -1123,6 +1225,7 @@ Vitaliy Vkey vlen voidpf +vp vvv wb werror @@ -1192,11 +1295,14 @@ xrefTable xsl XSLTPROC XXX +xy yn yuiop yyyymmdd z's zalloc +Zarko +Zarko's zdata Zeroize zeroizing diff --git a/libqpdf.map b/libqpdf.map index 857f56c..ab23bd9 100644 --- a/libqpdf.map +++ b/libqpdf.map @@ -1,4 +1,4 @@ -LIBQPDF_8 { +LIBQPDF_10 { global: *; }; diff --git a/libqpdf.pc.in b/libqpdf.pc.in index c765900..5d1a867 100644 --- a/libqpdf.pc.in +++ b/libqpdf.pc.in @@ -6,5 +6,6 @@ includedir=@includedir@ Name: libqpdf Description: PDF transformation library Version: @PACKAGE_VERSION@ +Requires.private: zlib, libpcre Libs: -L${libdir} -lqpdf Cflags: -I${includedir} diff --git a/libqpdf/OffsetInputSource.cc b/libqpdf/OffsetInputSource.cc new file mode 100644 index 0000000..c1ec410 --- /dev/null +++ b/libqpdf/OffsetInputSource.cc @@ -0,0 +1,61 @@ +#include + +OffsetInputSource::OffsetInputSource(PointerHolder proxied, + qpdf_offset_t global_offset) : + proxied(proxied), + global_offset(global_offset) +{ +} + +OffsetInputSource::~OffsetInputSource() +{ +} + +qpdf_offset_t +OffsetInputSource::findAndSkipNextEOL() +{ + return this->proxied->findAndSkipNextEOL() - this->global_offset; +} + +std::string const& +OffsetInputSource::getName() const +{ + return this->proxied->getName(); +} + +qpdf_offset_t +OffsetInputSource::tell() +{ + return this->proxied->tell() - this->global_offset; +} + +void +OffsetInputSource::seek(qpdf_offset_t offset, int whence) +{ + if (whence == SEEK_SET) + { + this->proxied->seek(offset + global_offset, whence); + } + else + { + this->proxied->seek(offset, whence); + } +} + +void +OffsetInputSource::rewind() +{ + seek(0, SEEK_SET); +} + +size_t +OffsetInputSource::read(char* buffer, size_t length) +{ + return this->proxied->read(buffer, length); +} + +void +OffsetInputSource::unreadCh(char ch) +{ + this->proxied->unreadCh(ch); +} diff --git a/libqpdf/Pl_AES_PDF.cc b/libqpdf/Pl_AES_PDF.cc index 0f73c09..5287610 100644 --- a/libqpdf/Pl_AES_PDF.cc +++ b/libqpdf/Pl_AES_PDF.cc @@ -6,28 +6,29 @@ #include #include #include -#include -#ifndef HAVE_RANDOM -# define random rand -# define srandom srand -#endif bool Pl_AES_PDF::use_static_iv = false; Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, - bool encrypt, unsigned char const key[key_size]) : + bool encrypt, unsigned char const* key, + unsigned int key_bytes) : Pipeline(identifier, next), encrypt(encrypt), cbc_mode(true), first(true), offset(0), - nrounds(0) + nrounds(0), + use_zero_iv(false), + use_specified_iv(false), + disable_padding(false) { - static int const keybits = 128; - assert(key_size == KEYLENGTH(keybits)); - assert(sizeof(this->rk) / sizeof(uint32_t) == RKLENGTH(keybits)); - std::memcpy(this->key, key, key_size); - std::memset(this->rk, 0, sizeof(this->rk)); + unsigned int keybits = 8 * key_bytes; + assert(key_bytes == KEYLENGTH(keybits)); + this->key = new unsigned char[key_bytes]; + this->rk = new uint32_t[RKLENGTH(keybits)]; + unsigned int rk_bytes = RKLENGTH(keybits) * sizeof(uint32_t); + std::memcpy(this->key, key, key_bytes); + std::memset(this->rk, 0, rk_bytes); std::memset(this->inbuf, 0, this->buf_size); std::memset(this->outbuf, 0, this->buf_size); std::memset(this->cbc_block, 0, this->buf_size); @@ -44,7 +45,33 @@ Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, Pl_AES_PDF::~Pl_AES_PDF() { - // nothing needed + delete [] this->key; + delete [] this->rk; +} + +void +Pl_AES_PDF::useZeroIV() +{ + this->use_zero_iv = true; +} + +void +Pl_AES_PDF::disablePadding() +{ + this->disable_padding = true; +} + +void +Pl_AES_PDF::setIV(unsigned char const* iv, size_t bytes) +{ + if (bytes != this->buf_size) + { + throw std::logic_error( + "Pl_AES_PDF: specified initialization vector" + " size in bytes must be " + QUtil::int_to_string(bytes)); + } + this->use_specified_iv = true; + memcpy(this->specified_iv, iv, bytes); } void @@ -90,13 +117,16 @@ Pl_AES_PDF::finish() { flush(false); } - // Pad as described in section 3.5.1 of version 1.7 of the PDF - // specification, including providing an entire block of padding - // if the input was a multiple of 16 bytes. - unsigned char pad = (unsigned char) (this->buf_size - this->offset); - memset(this->inbuf + this->offset, pad, pad); - this->offset = this->buf_size; - flush(false); + if (! this->disable_padding) + { + // Pad as described in section 3.5.1 of version 1.7 of the PDF + // specification, including providing an entire block of padding + // if the input was a multiple of 16 bytes. + unsigned char pad = (unsigned char) (this->buf_size - this->offset); + memset(this->inbuf + this->offset, pad, pad); + this->offset = this->buf_size; + flush(false); + } } else { @@ -112,7 +142,7 @@ Pl_AES_PDF::finish() this->buf_size - this->offset); this->offset = this->buf_size; } - flush(true); + flush(! this->disable_padding); } getNext()->finish(); } @@ -120,16 +150,18 @@ Pl_AES_PDF::finish() void Pl_AES_PDF::initializeVector() { - static bool seeded_random = false; - if (! seeded_random) + if (use_zero_iv) { - // Seed the random number generator with something simple, but - // just to be interesting, don't use the unmodified current - // time.... - srandom((int)QUtil::get_current_time() ^ 0xcccc); - seeded_random = true; + for (unsigned int i = 0; i < this->buf_size; ++i) + { + this->cbc_block[i] = 0; + } } - if (use_static_iv) + else if (use_specified_iv) + { + std::memcpy(this->cbc_block, this->specified_iv, this->buf_size); + } + else if (use_static_iv) { for (unsigned int i = 0; i < this->buf_size; ++i) { @@ -138,10 +170,7 @@ Pl_AES_PDF::initializeVector() } else { - for (unsigned int i = 0; i < this->buf_size; ++i) - { - this->cbc_block[i] = (unsigned char)((random() & 0xff0) >> 4); - } + QUtil::initializeWithRandomBytes(this->cbc_block, this->buf_size); } } @@ -157,12 +186,21 @@ Pl_AES_PDF::flush(bool strip_padding) { if (encrypt) { - // Set cbc_block to a random initialization vector and - // write it to the output stream + // Set cbc_block to the initialization vector, and if + // not zero, write it to the output stream. initializeVector(); - getNext()->write(this->cbc_block, this->buf_size); + if (! (this->use_zero_iv || this->use_specified_iv)) + { + getNext()->write(this->cbc_block, this->buf_size); + } } - else + else if (this->use_zero_iv || this->use_specified_iv) + { + // Initialize vector with zeroes; zero vector was not + // written to the beginning of the input file. + initializeVector(); + } + else { // Take the first block of input as the initialization // vector. There's nothing to write at this time. diff --git a/libqpdf/Pl_SHA2.cc b/libqpdf/Pl_SHA2.cc new file mode 100644 index 0000000..018f411 --- /dev/null +++ b/libqpdf/Pl_SHA2.cc @@ -0,0 +1,164 @@ +#include +#include +#include +#include + +Pl_SHA2::Pl_SHA2(int bits, Pipeline* next) : + Pipeline("sha2", next), + in_progress(false), + bits(0) +{ + if (bits) + { + resetBits(bits); + } +} + +Pl_SHA2::~Pl_SHA2() +{ +} + +void +Pl_SHA2::badBits() +{ + throw std::logic_error("Pl_SHA2 has unexpected value for bits"); +} + +void +Pl_SHA2::write(unsigned char* buf, size_t len) +{ + if (! this->in_progress) + { + switch (bits) + { + case 256: + sph_sha256_init(&this->ctx256); + break; + case 384: + sph_sha384_init(&this->ctx384); + break; + case 512: + sph_sha512_init(&this->ctx512); + break; + default: + badBits(); + break; + } + this->in_progress = true; + } + + // Write in chunks in case len is too big to fit in an int. + // Assume int is at least 32 bits. + static size_t const max_bytes = 1 << 30; + size_t bytes_left = len; + unsigned char* data = buf; + while (bytes_left > 0) + { + size_t bytes = (bytes_left >= max_bytes ? max_bytes : bytes_left); + switch (bits) + { + case 256: + sph_sha256(&this->ctx256, data, bytes); + break; + case 384: + sph_sha384(&this->ctx384, data, bytes); + break; + case 512: + sph_sha512(&this->ctx512, data, bytes); + break; + default: + badBits(); + break; + } + bytes_left -= bytes; + data += bytes; + } + + if (this->getNext(true)) + { + this->getNext()->write(buf, len); + } +} + +void +Pl_SHA2::finish() +{ + if (this->getNext(true)) + { + this->getNext()->finish(); + } + switch (bits) + { + case 256: + sph_sha256_close(&this->ctx256, sha256sum); + break; + case 384: + sph_sha384_close(&this->ctx384, sha384sum); + break; + case 512: + sph_sha512_close(&this->ctx512, sha512sum); + break; + default: + badBits(); + break; + } + this->in_progress = false; +} + +void +Pl_SHA2::resetBits(int bits) +{ + if (this->in_progress) + { + throw std::logic_error( + "bit reset requested for in-progress SHA2 Pipeline"); + } + if (! ((bits == 256) || (bits == 384) || (bits == 512))) + { + throw std::logic_error("Pl_SHA2 called with bits != 256, 384, or 512"); + } + this->bits = bits; +} + +std::string +Pl_SHA2::getRawDigest() +{ + std::string result; + switch (bits) + { + case 256: + result = std::string((char*)this->sha256sum, sizeof(this->sha256sum)); + break; + case 384: + result = std::string((char*)this->sha384sum, sizeof(this->sha384sum)); + break; + case 512: + result = std::string((char*)this->sha512sum, sizeof(this->sha512sum)); + break; + default: + badBits(); + break; + } + return result; +} + +std::string +Pl_SHA2::getHexDigest() +{ + if (this->in_progress) + { + throw std::logic_error( + "digest requested for in-progress SHA2 Pipeline"); + } + std::string raw = getRawDigest(); + size_t raw_size = raw.length(); + size_t hex_size = 1 + (2 * raw_size); + PointerHolder bufp(true, new char[hex_size]); + char* buf = bufp.getPointer(); + buf[hex_size - 1] = '\0'; + for (unsigned int i = 0; i < raw_size; ++i) + { + std::sprintf(buf + i * 2, "%02x", (unsigned char)raw[i]); + } + return buf; +} diff --git a/libqpdf/QPDF.cc b/libqpdf/QPDF.cc index efef337..a779c23 100644 --- a/libqpdf/QPDF.cc +++ b/libqpdf/QPDF.cc @@ -13,12 +13,13 @@ #include #include #include +#include #include #include #include -std::string QPDF::qpdf_version = "3.0.2"; +std::string QPDF::qpdf_version = "4.0.0"; static char const* EMPTY_PDF = "%PDF-1.3\n" @@ -96,6 +97,7 @@ QPDF::QPDF() : err_stream(&std::cerr), attempt_recovery(true), encryption_V(0), + encryption_R(0), encrypt_metadata(true), cf_stream(e_none), cf_string(e_none), @@ -138,9 +140,8 @@ void QPDF::processFile(char const* filename, char const* password) { FileInputSource* fi = new FileInputSource(); - this->file = fi; fi->setFilename(filename); - parse(password); + processInputSource(fi, password); } void @@ -148,9 +149,8 @@ QPDF::processFile(char const* description, FILE* filep, bool close_file, char const* password) { FileInputSource* fi = new FileInputSource(); - this->file = fi; fi->setFile(description, filep, close_file); - parse(password); + processInputSource(fi, password); } void @@ -158,10 +158,18 @@ QPDF::processMemoryFile(char const* description, char const* buf, size_t length, char const* password) { - this->file = + processInputSource( new BufferInputSource(description, new Buffer((unsigned char*)buf, length), - true); + true), + password); +} + +void +QPDF::processInputSource(PointerHolder source, + char const* password) +{ + this->file = source; parse(password); } @@ -207,7 +215,7 @@ QPDF::getWarnings() void QPDF::parse(char const* password) { - PCRE header_re("^%PDF-(1.\\d+)\\b"); + PCRE header_re("\\A((?s).*?)%PDF-(1.\\d+)\\b"); PCRE eof_re("(?s:startxref\\s+(\\d+)\\s+%%EOF\\b)"); if (password) @@ -215,11 +223,26 @@ QPDF::parse(char const* password) this->provided_password = password; } - std::string line = this->file->readLine(20); + // Find the header anywhere in the first 1024 bytes of the file, + // plus add a little extra space for the header itself. + char buffer[1045]; + memset(buffer, '\0', sizeof(buffer)); + this->file->read(buffer, sizeof(buffer) - 1); + std::string line(buffer); PCRE::Match m1 = header_re.match(line.c_str()); if (m1) { - this->pdf_version = m1.getMatch(1); + size_t global_offset = m1.getMatch(1).length(); + if (global_offset != 0) + { + // Empirical evidence strongly suggests that when there is + // leading material prior to the PDF header, all explicit + // offsets in the file are such that 0 points to the + // beginning of the header. + QTC::TC("qpdf", "QPDF global offset"); + this->file = new OffsetInputSource(this->file, global_offset); + } + this->pdf_version = m1.getMatch(2); if (atof(this->pdf_version.c_str()) < 1.2) { this->tokenizer.allowPoundAnywhereInName(); @@ -292,6 +315,7 @@ QPDF::parse(char const* password) } initializeEncryption(); + findAttachmentStreams(); } void @@ -1247,6 +1271,21 @@ QPDF::readObjectAtOffset(bool try_recovery, int& objid, int& generation) { setLastObjectDescription(description, exp_objid, exp_generation); + + // Special case: if offset is 0, just return null. Some PDF + // writers, in particular "Mac OS X 10.7.5 Quartz PDFContext", may + // store deleted objects in the xref table as "0000000000 00000 + // n", which is not correct, but it won't hurt anything for to + // ignore these. + if (offset == 0) + { + QTC::TC("qpdf", "QPDF bogus 0 offset", 0); + warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + this->last_object_description, 0, + "object has offset 0")); + return QPDFObjectHandle::newNull(); + } + this->file->seek(offset, SEEK_SET); QPDFTokenizer::Token tobjid = readToken(this->file); @@ -1823,28 +1862,6 @@ QPDF::swapObjects(int objid1, int generation1, int objid2, int generation2) this->obj_cache[og2] = t; } -void -QPDF::trimTrailerForWrite() -{ - // Note that removing the encryption dictionary does not interfere - // with reading encrypted files. QPDF loads all the information - // it needs from the encryption dictionary at the beginning and - // never looks at it again. - this->trailer.removeKey("/ID"); - this->trailer.removeKey("/Encrypt"); - this->trailer.removeKey("/Prev"); - - // Remove all trailer keys that potentially come from a - // cross-reference stream - this->trailer.removeKey("/Index"); - this->trailer.removeKey("/W"); - this->trailer.removeKey("/Length"); - this->trailer.removeKey("/Filter"); - this->trailer.removeKey("/DecodeParms"); - this->trailer.removeKey("/Type"); - this->trailer.removeKey("/XRefStm"); -} - std::string QPDF::getFilename() const { @@ -1857,6 +1874,30 @@ QPDF::getPDFVersion() const return this->pdf_version; } +int +QPDF::getExtensionLevel() +{ + int result = 0; + QPDFObjectHandle obj = getRoot(); + if (obj.hasKey("/Extensions")) + { + obj = obj.getKey("/Extensions"); + if (obj.isDictionary() && obj.hasKey("/ADBE")) + { + obj = obj.getKey("/ADBE"); + if (obj.isDictionary() && obj.hasKey("/ExtensionLevel")) + { + obj = obj.getKey("/ExtensionLevel"); + if (obj.isInteger()) + { + result = obj.getIntValue(); + } + } + } + } + return result; +} + QPDFObjectHandle QPDF::getTrailer() { @@ -2032,18 +2073,36 @@ QPDF::pipeStreamData(int objid, int generation, } void -QPDF::decodeStreams() +QPDF::findAttachmentStreams() { - for (std::map::iterator iter = - this->xref_table.begin(); - iter != this->xref_table.end(); ++iter) + QPDFObjectHandle root = getRoot(); + QPDFObjectHandle names = root.getKey("/Names"); + if (! names.isDictionary()) { - ObjGen const& og = (*iter).first; - QPDFObjectHandle obj = getObjectByID(og.obj, og.gen); - if (obj.isStream()) - { - Pl_Discard pl; - obj.pipeStreamData(&pl, true, false, false); - } + return; + } + QPDFObjectHandle embeddedFiles = names.getKey("/EmbeddedFiles"); + if (! embeddedFiles.isDictionary()) + { + return; + } + names = embeddedFiles.getKey("/Names"); + if (! names.isArray()) + { + return; + } + for (int i = 0; i < names.getArrayNItems(); ++i) + { + QPDFObjectHandle item = names.getArrayItem(i); + if (item.isDictionary() && + item.getKey("/Type").isName() && + (item.getKey("/Type").getName() == "/Filespec") && + item.getKey("/EF").isDictionary() && + item.getKey("/EF").getKey("/F").isStream()) + { + QPDFObjectHandle stream = item.getKey("/EF").getKey("/F"); + this->attachment_streams.insert( + ObjGen(stream.getObjectID(), stream.getGeneration())); + } } } diff --git a/libqpdf/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index eb08488..a1949a9 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -64,6 +64,11 @@ QPDFWriter::init() object_stream_mode = qpdf_o_preserve; encrypt_metadata = true; encrypt_use_aes = false; + min_extension_level = 0; + final_extension_level = 0; + forced_extension_level = 0; + encryption_V = 0; + encryption_R = 0; encryption_dict_objid = 0; next_objid = 1; cur_stream_length_id = 0; @@ -135,6 +140,13 @@ QPDFWriter::getBuffer() } void +QPDFWriter::setOutputPipeline(Pipeline* p) +{ + this->filename = "custom pipeline"; + initializePipelineStack(p); +} + +void QPDFWriter::setObjectStreamMode(qpdf_object_stream_e mode) { this->object_stream_mode = mode; @@ -163,10 +175,19 @@ QPDFWriter::setQDFMode(bool val) void QPDFWriter::setMinimumPDFVersion(std::string const& version) { + setMinimumPDFVersion(version, 0); +} + +void +QPDFWriter::setMinimumPDFVersion(std::string const& version, + int extension_level) +{ bool set_version = false; + bool set_extension_level = false; if (this->min_pdf_version.empty()) { set_version = true; + set_extension_level = true; } else { @@ -176,10 +197,22 @@ QPDFWriter::setMinimumPDFVersion(std::string const& version) int min_minor = 0; parseVersion(version, old_major, old_minor); parseVersion(this->min_pdf_version, min_major, min_minor); - if (compareVersions(old_major, old_minor, min_major, min_minor) > 0) + int compare = compareVersions( + old_major, old_minor, min_major, min_minor); + if (compare > 0) { - QTC::TC("qpdf", "QPDFWriter increasing minimum version"); + QTC::TC("qpdf", "QPDFWriter increasing minimum version", + extension_level == 0 ? 0 : 1); set_version = true; + set_extension_level = true; + } + else if (compare == 0) + { + if (extension_level > this->min_extension_level) + { + QTC::TC("qpdf", "QPDFWriter increasing extension level"); + set_extension_level = true; + } } } @@ -187,12 +220,24 @@ QPDFWriter::setMinimumPDFVersion(std::string const& version) { this->min_pdf_version = version; } + if (set_extension_level) + { + this->min_extension_level = extension_level; + } } void QPDFWriter::forcePDFVersion(std::string const& version) { + forcePDFVersion(version, 0); +} + +void +QPDFWriter::forcePDFVersion(std::string const& version, + int extension_level) +{ this->forced_pdf_version = version; + this->forced_extension_level = extension_level; } void @@ -301,6 +346,38 @@ QPDFWriter::setR4EncryptionParameters( } void +QPDFWriter::setR5EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + bool encrypt_metadata) +{ + std::set clear; + interpretR3EncryptionParameters( + clear, user_password, owner_password, + allow_accessibility, allow_extract, print, modify); + this->encrypt_use_aes = true; + this->encrypt_metadata = encrypt_metadata; + setEncryptionParameters(user_password, owner_password, 5, 5, 32, clear); +} + +void +QPDFWriter::setR6EncryptionParameters( + char const* user_password, char const* owner_password, + bool allow_accessibility, bool allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + bool encrypt_metadata) +{ + std::set clear; + interpretR3EncryptionParameters( + clear, user_password, owner_password, + allow_accessibility, allow_extract, print, modify); + this->encrypt_use_aes = true; + this->encrypt_metadata = encrypt_metadata; + setEncryptionParameters(user_password, owner_password, 5, 6, 32, clear); +} + +void QPDFWriter::interpretR3EncryptionParameters( std::set& clear, char const* user_password, char const* owner_password, @@ -383,6 +460,12 @@ QPDFWriter::setEncryptionParameters( bits_to_clear.insert(1); bits_to_clear.insert(2); + if (R > 3) + { + // Bit 10 is deprecated and should always be set. + bits_to_clear.erase(10); + } + int P = 0; // Create the complement of P, then invert. for (std::set::iterator iter = bits_to_clear.begin(); @@ -395,11 +478,26 @@ QPDFWriter::setEncryptionParameters( generateID(); std::string O; std::string U; - QPDF::compute_encryption_O_U( - user_password, owner_password, V, R, key_len, P, - this->encrypt_metadata, this->id1, O, U); + std::string OE; + std::string UE; + std::string Perms; + std::string encryption_key; + if (V < 5) + { + QPDF::compute_encryption_O_U( + user_password, owner_password, V, R, key_len, P, + this->encrypt_metadata, this->id1, O, U); + } + else + { + QPDF::compute_encryption_parameters_V5( + user_password, owner_password, V, R, key_len, P, + this->encrypt_metadata, this->id1, + encryption_key, O, U, OE, UE, Perms); + } setEncryptionParametersInternal( - V, R, key_len, P, O, U, this->id1, user_password); + V, R, key_len, P, O, U, OE, UE, Perms, + this->id1, user_password, encryption_key); } void @@ -427,32 +525,31 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf) } if (V >= 4) { - if (encrypt.hasKey("/CF") && - encrypt.getKey("/CF").isDictionary() && - encrypt.hasKey("/StmF") && - encrypt.getKey("/StmF").isName()) - { - // Determine whether to use AES from StmF. QPDFWriter - // can't write files with different StrF and StmF. - QPDFObjectHandle CF = encrypt.getKey("/CF"); - QPDFObjectHandle StmF = encrypt.getKey("/StmF"); - if (CF.hasKey(StmF.getName()) && - CF.getKey(StmF.getName()).isDictionary()) - { - QPDFObjectHandle StmF_data = CF.getKey(StmF.getName()); - if (StmF_data.hasKey("/CFM") && - StmF_data.getKey("/CFM").isName() && - StmF_data.getKey("/CFM").getName() == "/AESV2") - { - this->encrypt_use_aes = true; - } - } - } + // When copying encryption parameters, use AES even if the + // original file did not. Acrobat doesn't create files + // with V >= 4 that don't use AES, and the logic of + // figuring out whether AES is used or not is complicated + // with /StmF, /StrF, and /EFF all potentially having + // different values. + this->encrypt_use_aes = true; } QTC::TC("qpdf", "QPDFWriter copy encrypt metadata", this->encrypt_metadata ? 0 : 1); QTC::TC("qpdf", "QPDFWriter copy use_aes", this->encrypt_use_aes ? 0 : 1); + std::string OE; + std::string UE; + std::string Perms; + std::string encryption_key; + if (V >= 5) + { + QTC::TC("qpdf", "QPDFWriter copy V5"); + OE = encrypt.getKey("/OE").getStringValue(); + UE = encrypt.getKey("/UE").getStringValue(); + Perms = encrypt.getKey("/Perms").getStringValue(); + encryption_key = qpdf.getEncryptionKey(); + } + setEncryptionParametersInternal( V, encrypt.getKey("/R").getIntValue(), @@ -460,13 +557,18 @@ QPDFWriter::copyEncryptionParameters(QPDF& qpdf) encrypt.getKey("/P").getIntValue(), encrypt.getKey("/O").getStringValue(), encrypt.getKey("/U").getStringValue(), + OE, + UE, + Perms, this->id1, // this->id1 == the other file's id1 - qpdf.getPaddedUserPassword()); + qpdf.getPaddedUserPassword(), + encryption_key); } } void -QPDFWriter::disableIncompatibleEncryption(int major, int minor) +QPDFWriter::disableIncompatibleEncryption(int major, int minor, + int extension_level) { if (! this->encrypted) { @@ -503,6 +605,15 @@ QPDFWriter::disableIncompatibleEncryption(int major, int minor) disable = true; } } + else if ((compareVersions(major, minor, 1, 7) < 0) || + ((compareVersions(major, minor, 1, 7) == 0) && + extension_level < 3)) + { + if ((V >= 5) || (R >= 5)) + { + disable = true; + } + } } if (disable) { @@ -562,8 +673,12 @@ void QPDFWriter::setEncryptionParametersInternal( int V, int R, int key_len, long P, std::string const& O, std::string const& U, - std::string const& id1, std::string const& user_password) + std::string const& OE, std::string const& UE, std::string const& Perms, + std::string const& id1, std::string const& user_password, + std::string const& encryption_key) { + this->encryption_V = V; + this->encryption_R = R; encryption_dictionary["/Filter"] = "/Standard"; encryption_dictionary["/V"] = QUtil::int_to_string(V); encryption_dictionary["/Length"] = QUtil::int_to_string(key_len * 8); @@ -571,44 +686,71 @@ QPDFWriter::setEncryptionParametersInternal( encryption_dictionary["/P"] = QUtil::int_to_string(P); encryption_dictionary["/O"] = QPDF_String(O).unparse(true); encryption_dictionary["/U"] = QPDF_String(U).unparse(true); - setMinimumPDFVersion("1.3"); - if (R == 3) + if (V >= 5) { - setMinimumPDFVersion("1.4"); + encryption_dictionary["/OE"] = QPDF_String(OE).unparse(true); + encryption_dictionary["/UE"] = QPDF_String(UE).unparse(true); + encryption_dictionary["/Perms"] = QPDF_String(Perms).unparse(true); + } + if (R >= 6) + { + setMinimumPDFVersion("1.7", 8); + } + else if (R == 5) + { + setMinimumPDFVersion("1.7", 3); } - else if (R >= 4) + else if (R == 4) { setMinimumPDFVersion(this->encrypt_use_aes ? "1.6" : "1.5"); } + else if (R == 3) + { + setMinimumPDFVersion("1.4"); + } + else + { + setMinimumPDFVersion("1.3"); + } if ((R >= 4) && (! encrypt_metadata)) { encryption_dictionary["/EncryptMetadata"] = "false"; } - if (V == 4) + if ((V == 4) || (V == 5)) { // The spec says the value for the crypt filter key can be // anything, and xpdf seems to agree. However, Adobe Reader // won't open our files unless we use /StdCF. encryption_dictionary["/StmF"] = "/StdCF"; encryption_dictionary["/StrF"] = "/StdCF"; - std::string method = (this->encrypt_use_aes ? "/AESV2" : "/V2"); + std::string method = (this->encrypt_use_aes + ? ((V < 5) ? "/AESV2" : "/AESV3") + : "/V2"); encryption_dictionary["/CF"] = "<< /StdCF << /AuthEvent /DocOpen /CFM " + method + " >> >>"; } this->encrypted = true; QPDF::EncryptionData encryption_data( - V, R, key_len, P, O, U, id1, this->encrypt_metadata); - this->encryption_key = QPDF::compute_encryption_key( - user_password, encryption_data); + V, R, key_len, P, O, U, OE, UE, Perms, id1, this->encrypt_metadata); + if (V < 5) + { + this->encryption_key = QPDF::compute_encryption_key( + user_password, encryption_data); + } + else + { + this->encryption_key = encryption_key; + } } void QPDFWriter::setDataKey(int objid) { this->cur_data_key = QPDF::compute_data_key( - this->encryption_key, objid, 0, this->encrypt_use_aes); + this->encryption_key, objid, 0, + this->encrypt_use_aes, this->encryption_V, this->encryption_R); } int @@ -745,13 +887,14 @@ QPDFWriter::pushEncryptionFilter() { p = new Pl_AES_PDF( "aes stream encryption", this->pipeline, true, - (unsigned char*) this->cur_data_key.c_str()); + (unsigned char*) this->cur_data_key.c_str(), + (unsigned int)this->cur_data_key.length()); } else { p = new Pl_RC4("rc4 stream encryption", this->pipeline, (unsigned char*) this->cur_data_key.c_str(), - (int)this->cur_data_key.length()); + (unsigned int)this->cur_data_key.length()); } pushPipeline(p); } @@ -827,16 +970,6 @@ QPDFWriter::enqueueObject(QPDFObjectHandle object) { // This is a place-holder object for an object stream } - else if (object.isScalar()) - { - // flattenScalarReferences is supposed to have removed all - // indirect scalars. - throw std::logic_error( - "INTERNAL ERROR: QPDFWriter::enqueueObject: indirect scalar: " + - std::string(this->filename) + " " + - QUtil::int_to_string(object.getObjectID()) + " " + - QUtil::int_to_string(object.getGeneration())); - } int objid = object.getObjectID(); if (obj_renumber.count(objid) == 0) @@ -909,15 +1042,6 @@ QPDFWriter::unparseChild(QPDFObjectHandle child, int level, int flags) } if (child.isIndirect()) { - if (child.isScalar()) - { - // flattenScalarReferences is supposed to have removed all - // indirect scalars. - throw std::logic_error( - "INTERNAL ERROR: QPDFWriter::unparseChild: indirect scalar: " + - QUtil::int_to_string(child.getObjectID()) + " " + - QUtil::int_to_string(child.getGeneration())); - } int old_id = child.getObjectID(); int new_id = obj_renumber[old_id]; writeString(QUtil::int_to_string(new_id)); @@ -933,7 +1057,7 @@ void QPDFWriter::writeTrailer(trailer_e which, int size, bool xref_stream, qpdf_offset_t prev) { - QPDFObjectHandle trailer = pdf.getTrailer(); + QPDFObjectHandle trailer = getTrimmedTrailer(); if (! xref_stream) { writeString("trailer <<"); @@ -1011,6 +1135,7 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, unsigned int flags, size_t stream_length, bool compress) { + int old_id = object.getObjectID(); unsigned int child_flags = flags & ~f_stream; std::string indent; @@ -1043,32 +1168,199 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, } else if (object.isDictionary()) { + // Make a shallow copy of this object so we can modify it + // safely without affecting the original. This code makes + // assumptions about things that are made true in + // prepareFileForWrite, such as that certain things are direct + // objects so that replacing them doesn't leave unreferenced + // objects in the output. + object = object.shallowCopy(); + + // Handle special cases for specific dictionaries. + + // Extensions dictionaries. + + // We have one of several cases: + // + // * We need ADBE + // - We already have Extensions + // - If it has the right ADBE, preserve it + // - Otherwise, replace ADBE + // - We don't have Extensions: create one from scratch + // * We don't want ADBE + // - We already have Extensions + // - If it only has ADBE, remove it + // - If it has other things, keep those and remove ADBE + // - We have no extensions: no action required + // + // Before writing, we guarantee that /Extensions, if present, + // is direct through the ADBE dictionary, so we can modify in + // place. + + bool is_root = false; + bool have_extensions_other = false; + bool have_extensions_adbe = false; + + QPDFObjectHandle extensions; + if (old_id == pdf.getRoot().getObjectID()) + { + is_root = true; + if (object.hasKey("/Extensions") && + object.getKey("/Extensions").isDictionary()) + { + extensions = object.getKey("/Extensions"); + } + } + + if (extensions.isInitialized()) + { + std::set keys = extensions.getKeys(); + if (keys.count("/ADBE") > 0) + { + have_extensions_adbe = true; + keys.erase("/ADBE"); + } + if (keys.size() > 0) + { + have_extensions_other = true; + } + } + + bool need_extensions_adbe = (this->final_extension_level > 0); + + if (is_root) + { + if (need_extensions_adbe) + { + if (! (have_extensions_other || have_extensions_adbe)) + { + // We need Extensions and don't have it. Create + // it here. + QTC::TC("qpdf", "QPDFWriter create Extensions", + this->qdf_mode ? 0 : 1); + extensions = QPDFObjectHandle::newDictionary(); + object.replaceKey("/Extensions", extensions); + } + } + else if (! have_extensions_other) + { + // We have Extensions dictionary and don't want one. + if (have_extensions_adbe) + { + QTC::TC("qpdf", "QPDFWriter remove existing Extensions"); + object.removeKey("/Extensions"); + extensions = QPDFObjectHandle(); // uninitialized + } + } + } + + if (extensions.isInitialized()) + { + QTC::TC("qpdf", "QPDFWriter preserve Extensions"); + QPDFObjectHandle adbe = extensions.getKey("/ADBE"); + if (adbe.isDictionary() && + adbe.hasKey("/BaseVersion") && + adbe.getKey("/BaseVersion").isName() && + (adbe.getKey("/BaseVersion").getName() == + "/" + this->final_pdf_version) && + adbe.hasKey("/ExtensionLevel") && + adbe.getKey("/ExtensionLevel").isInteger() && + (adbe.getKey("/ExtensionLevel").getIntValue() == + this->final_extension_level)) + { + QTC::TC("qpdf", "QPDFWriter preserve ADBE"); + } + else + { + if (need_extensions_adbe) + { + extensions.replaceKey( + "/ADBE", + QPDFObjectHandle::parse( + "<< /BaseVersion /" + this->final_pdf_version + + " /ExtensionLevel " + + QUtil::int_to_string(this->final_extension_level) + + " >>")); + } + else + { + QTC::TC("qpdf", "QPDFWriter remove ADBE"); + extensions.removeKey("/ADBE"); + } + } + } + + // Stream dictionaries. + + if (flags & f_stream) + { + // Suppress /Length since we will write it manually + object.removeKey("/Length"); + + if (flags & f_filtered) + { + // We will supply our own filter and decode + // parameters. + object.removeKey("/Filter"); + object.removeKey("/DecodeParms"); + } + else + { + // Make sure, no matter what else we have, that we + // don't have /Crypt in the output filters. + QPDFObjectHandle filter = object.getKey("/Filter"); + QPDFObjectHandle decode_parms = object.getKey("/DecodeParms"); + if (filter.isOrHasName("/Crypt")) + { + if (filter.isName()) + { + object.removeKey("/Filter"); + object.removeKey("/DecodeParms"); + } + else + { + int idx = -1; + for (int i = 0; i < filter.getArrayNItems(); ++i) + { + QPDFObjectHandle item = filter.getArrayItem(i); + if (item.isName() && item.getName() == "/Crypt") + { + idx = i; + break; + } + } + if (idx >= 0) + { + // If filter is an array, then the code in + // QPDF_Stream has already verified that + // DecodeParms and Filters are arrays of + // the same length, but if they weren't + // for some reason, eraseItem does type + // and bounds checking. + QTC::TC("qpdf", "QPDFWriter remove Crypt"); + filter.eraseItem(idx); + decode_parms.eraseItem(idx); + } + } + } + } + } + writeString("<<"); writeStringQDF("\n"); + std::set keys = object.getKeys(); for (std::set::iterator iter = keys.begin(); iter != keys.end(); ++iter) { - // I'm not fully clear on /Crypt keys in /DecodeParms. If - // one is found, we refuse to filter, so we should be - // safe. std::string const& key = *iter; - if ((flags & f_filtered) && - ((key == "/Filter") || - (key == "/DecodeParms"))) - { - continue; - } - if ((flags & f_stream) && (key == "/Length")) - { - continue; - } + writeStringQDF(indent); writeStringQDF(" "); writeStringNoQDF(" "); writeString(QPDF_Name::normalizeName(key)); writeString(" "); - unparseChild(object.getKey(key), level + 1, child_flags); + unparseChild(object.getKey(key), level + 1, child_flags); writeStringQDF("\n"); } @@ -1105,7 +1397,6 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, else if (object.isStream()) { // Write stream data to a buffer. - int old_id = object.getObjectID(); int new_id = obj_renumber[old_id]; if (! this->direct_stream_lengths) { @@ -1214,7 +1505,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level, { Pl_Buffer bufpl("encrypted string"); Pl_AES_PDF pl("aes encrypt string", &bufpl, true, - (unsigned char const*)this->cur_data_key.c_str()); + (unsigned char const*)this->cur_data_key.c_str(), + (unsigned int)this->cur_data_key.length()); pl.write((unsigned char*) val.c_str(), val.length()); pl.finish(); Buffer* buf = bufpl.getBuffer(); @@ -1640,6 +1932,151 @@ QPDFWriter::generateObjectStreams() } } +QPDFObjectHandle +QPDFWriter::getTrimmedTrailer() +{ + // Remove keys from the trailer that necessarily have to be + // replaced when writing the file. + + QPDFObjectHandle trailer = pdf.getTrailer().shallowCopy(); + + // Remove encryption keys + trailer.removeKey("/ID"); + trailer.removeKey("/Encrypt"); + + // Remove modification information + trailer.removeKey("/Prev"); + + // Remove all trailer keys that potentially come from a + // cross-reference stream + trailer.removeKey("/Index"); + trailer.removeKey("/W"); + trailer.removeKey("/Length"); + trailer.removeKey("/Filter"); + trailer.removeKey("/DecodeParms"); + trailer.removeKey("/Type"); + trailer.removeKey("/XRefStm"); + + return trailer; +} + +void +QPDFWriter::prepareFileForWrite() +{ + // Do a traversal of the entire PDF file structure replacing all + // indirect objects that QPDFWriter wants to be direct. This + // includes stream lengths, stream filtering parameters, and + // document extension level information. Also replace all + // indirect null references with direct nulls. This way, the only + // indirect nulls queued for output will be object stream place + // holders. + + std::list queue; + queue.push_back(getTrimmedTrailer()); + std::set visited; + + while (! queue.empty()) + { + QPDFObjectHandle node = queue.front(); + queue.pop_front(); + if (node.isIndirect()) + { + if (visited.count(node.getObjectID()) > 0) + { + continue; + } + visited.insert(node.getObjectID()); + } + + if (node.isArray()) + { + int nitems = node.getArrayNItems(); + for (int i = 0; i < nitems; ++i) + { + QPDFObjectHandle oh = node.getArrayItem(i); + if (oh.isIndirect() && oh.isNull()) + { + QTC::TC("qpdf", "QPDFWriter flatten array null"); + oh.makeDirect(); + node.setArrayItem(i, oh); + } + else if (! oh.isScalar()) + { + queue.push_back(oh); + } + } + } + else if (node.isDictionary() || node.isStream()) + { + bool is_stream = false; + bool is_root = false; + QPDFObjectHandle dict = node; + if (node.isStream()) + { + is_stream = true; + dict = node.getDict(); + } + else if (pdf.getRoot().getObjectID() == node.getObjectID()) + { + is_root = true; + } + + std::set keys = dict.getKeys(); + for (std::set::iterator iter = keys.begin(); + iter != keys.end(); ++iter) + { + std::string const& key = *iter; + QPDFObjectHandle oh = dict.getKey(key); + bool add_to_queue = true; + if (is_stream) + { + if (oh.isIndirect() && + ((key == "/Length") || + (key == "/Filter") || + (key == "/DecodeParms"))) + { + QTC::TC("qpdf", "QPDFWriter make stream key direct"); + add_to_queue = false; + oh.makeDirect(); + dict.replaceKey(key, oh); + } + } + else if (is_root) + { + if ((key == "/Extensions") && (oh.isDictionary())) + { + bool extensions_indirect = false; + if (oh.isIndirect()) + { + QTC::TC("qpdf", "QPDFWriter make Extensions direct"); + extensions_indirect = true; + add_to_queue = false; + oh = oh.shallowCopy(); + dict.replaceKey(key, oh); + } + if (oh.hasKey("/ADBE")) + { + QPDFObjectHandle adbe = oh.getKey("/ADBE"); + if (adbe.isIndirect()) + { + QTC::TC("qpdf", "QPDFWriter make ADBE direct", + extensions_indirect ? 0 : 1); + adbe.makeDirect(); + oh.replaceKey("/ADBE", adbe); + } + } + } + } + + if (add_to_queue) + { + queue.push_back(oh); + } + } + } + } +} + void QPDFWriter::write() { @@ -1686,7 +2123,8 @@ QPDFWriter::write() int major = 0; int minor = 0; parseVersion(this->forced_pdf_version, major, minor); - disableIncompatibleEncryption(major, minor); + disableIncompatibleEncryption(major, minor, + this->forced_extension_level); if (compareVersions(major, minor, 1, 5) < 0) { QTC::TC("qpdf", "QPDFWriter forcing object stream disable"); @@ -1778,8 +2216,7 @@ QPDFWriter::write() generateID(); - pdf.trimTrailerForWrite(); - pdf.flattenScalarReferences(); + prepareFileForWrite(); if (this->linearized) { @@ -1834,16 +2271,18 @@ QPDFWriter::writeEncryptionDictionary() void QPDFWriter::writeHeader() { - setMinimumPDFVersion(pdf.getPDFVersion()); - std::string version = this->min_pdf_version; + setMinimumPDFVersion(pdf.getPDFVersion(), pdf.getExtensionLevel()); + this->final_pdf_version = this->min_pdf_version; + this->final_extension_level = this->min_extension_level; if (! this->forced_pdf_version.empty()) { QTC::TC("qpdf", "QPDFWriter using forced PDF version"); - version = this->forced_pdf_version; + this->final_pdf_version = this->forced_pdf_version; + this->final_extension_level = this->forced_extension_level; } writeString("%PDF-"); - writeString(version); + writeString(this->final_pdf_version); // This string of binary characters would not be valid UTF-8, so // it really should be treated as binary. writeString("\n%\xbf\xf7\xa2\xfe\n"); @@ -2427,7 +2866,7 @@ QPDFWriter::writeStandard() writeString(this->extra_header_text); // Put root first on queue. - QPDFObjectHandle trailer = pdf.getTrailer(); + QPDFObjectHandle trailer = getTrimmedTrailer(); enqueueObject(trailer.getKey("/Root")); // Next place any other objects referenced from the trailer diff --git a/libqpdf/QPDF_Stream.cc b/libqpdf/QPDF_Stream.cc index 970ee58..88b8e8f 100644 --- a/libqpdf/QPDF_Stream.cc +++ b/libqpdf/QPDF_Stream.cc @@ -91,6 +91,80 @@ QPDF_Stream::getRawStreamData() } bool +QPDF_Stream::understandDecodeParams( + std::string const& filter, QPDFObjectHandle decode_obj, + int& predictor, int& columns, bool& early_code_change) +{ + bool filterable = true; + std::set keys = decode_obj.getKeys(); + for (std::set::iterator iter = keys.begin(); + iter != keys.end(); ++iter) + { + std::string const& key = *iter; + if ((filter == "/FlateDecode") && (key == "/Predictor")) + { + QPDFObjectHandle predictor_obj = decode_obj.getKey(key); + if (predictor_obj.isInteger()) + { + predictor = predictor_obj.getIntValue(); + if (! ((predictor == 1) || (predictor == 12))) + { + filterable = false; + } + } + else + { + filterable = false; + } + } + else if ((filter == "/LZWDecode") && (key == "/EarlyChange")) + { + QPDFObjectHandle earlychange_obj = decode_obj.getKey(key); + if (earlychange_obj.isInteger()) + { + int earlychange = earlychange_obj.getIntValue(); + early_code_change = (earlychange == 1); + if (! ((earlychange == 0) || (earlychange == 1))) + { + filterable = false; + } + } + else + { + filterable = false; + } + } + else if (key == "/Columns") + { + QPDFObjectHandle columns_obj = decode_obj.getKey(key); + if (columns_obj.isInteger()) + { + columns = columns_obj.getIntValue(); + } + else + { + filterable = false; + } + } + else if ((filter == "/Crypt") && + (((key == "/Type") || (key == "/Name")) && + (decode_obj.getKey("/Type").isNull() || + (decode_obj.getKey("/Type").isName() && + (decode_obj.getKey("/Type").getName() == + "/CryptFilterDecodeParms"))))) + { + // we handle this in decryptStream + } + else + { + filterable = false; + } + } + + return filterable; +} + +bool QPDF_Stream::filterable(std::vector& filters, int& predictor, int& columns, bool& early_code_change) @@ -110,106 +184,6 @@ QPDF_Stream::filterable(std::vector& filters, filter_abbreviations["/DCT"] = "/DCTDecode"; } - // Initialize values to their defaults as per the PDF spec - predictor = 1; - columns = 0; - early_code_change = true; - - bool filterable = true; - - // See if we can support any decode parameters that are specified. - - QPDFObjectHandle decode_obj = - this->stream_dict.getKey("/DecodeParms"); - if (decode_obj.isNull()) - { - // no problem - } - else if (decode_obj.isDictionary()) - { - std::set keys = decode_obj.getKeys(); - for (std::set::iterator iter = keys.begin(); - iter != keys.end(); ++iter) - { - std::string const& key = *iter; - if (key == "/Predictor") - { - QPDFObjectHandle predictor_obj = decode_obj.getKey(key); - if (predictor_obj.isInteger()) - { - predictor = predictor_obj.getIntValue(); - if (! ((predictor == 1) || (predictor == 12))) - { - filterable = false; - } - } - else - { - filterable = false; - } - } - else if (key == "/EarlyChange") - { - QPDFObjectHandle earlychange_obj = decode_obj.getKey(key); - if (earlychange_obj.isInteger()) - { - int earlychange = earlychange_obj.getIntValue(); - early_code_change = (earlychange == 1); - if (! ((earlychange == 0) || (earlychange == 1))) - { - filterable = false; - } - } - else - { - filterable = false; - } - } - else if (key == "/Columns") - { - QPDFObjectHandle columns_obj = decode_obj.getKey(key); - if (columns_obj.isInteger()) - { - columns = columns_obj.getIntValue(); - } - else - { - filterable = false; - } - } - else if (((key == "/Type") || (key == "/Name")) && - decode_obj.getKey("/Type").isName() && - (decode_obj.getKey("/Type").getName() == - "/CryptFilterDecodeParms")) - { - // we handle this in decryptStream - } - else - { - filterable = false; - } - } - } - else - { - // Ignore for now -- some filter types, like CCITTFaxDecode, - // use types other than dictionary for this. - QTC::TC("qpdf", "QPDF_Stream ignore non-dictionary DecodeParms"); - - filterable = false; - } - - if ((predictor > 1) && (columns == 0)) - { - // invalid - filterable = false; - } - - if (! filterable) - { - return false; - } - // Check filters QPDFObjectHandle filter_obj = this->stream_dict.getKey("/Filter"); @@ -254,8 +228,7 @@ QPDF_Stream::filterable(std::vector& filters, "stream filter type is not name or array"); } - // `filters' now contains a list of filters to be applied in - // order. See which ones we can support. + bool filterable = true; for (std::vector::iterator iter = filters.begin(); iter != filters.end(); ++iter) @@ -278,6 +251,79 @@ QPDF_Stream::filterable(std::vector& filters, } } + if (! filterable) + { + return false; + } + + // `filters' now contains a list of filters to be applied in + // order. See which ones we can support. + + // Initialize values to their defaults as per the PDF spec + predictor = 1; + columns = 0; + early_code_change = true; + + // See if we can support any decode parameters that are specified. + + QPDFObjectHandle decode_obj = this->stream_dict.getKey("/DecodeParms"); + std::vector decode_parms; + if (decode_obj.isArray()) + { + for (int i = 0; i < decode_obj.getArrayNItems(); ++i) + { + decode_parms.push_back(decode_obj.getArrayItem(i)); + } + } + else + { + for (unsigned int i = 0; i < filters.size(); ++i) + { + decode_parms.push_back(decode_obj); + } + } + + if (decode_parms.size() != filters.size()) + { + throw QPDFExc(qpdf_e_damaged_pdf, qpdf->getFilename(), + "", this->offset, + "stream /DecodeParms length is" + " inconsistent with filters"); + } + + for (unsigned int i = 0; i < filters.size(); ++i) + { + QPDFObjectHandle decode_item = decode_parms[i]; + if (decode_item.isNull()) + { + // okay + } + else if (decode_item.isDictionary()) + { + if (! understandDecodeParams( + filters[i], decode_item, + predictor, columns, early_code_change)) + { + filterable = false; + } + } + else + { + filterable = false; + } + } + + if ((predictor > 1) && (columns == 0)) + { + // invalid + filterable = false; + } + + if (! filterable) + { + return false; + } + return filterable; } diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index c73a47b..60d54b7 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include @@ -23,11 +24,110 @@ static unsigned char const padding_string[] = { 0x2f, 0x0c, 0xa9, 0xfe, 0x64, 0x53, 0x69, 0x7a }; -static unsigned int const O_key_bytes = sizeof(MD5::Digest); static unsigned int const key_bytes = 32; +// V4 key lengths apply to V <= 4 +static unsigned int const OU_key_bytes_V4 = sizeof(MD5::Digest); + +static unsigned int const OU_key_bytes_V5 = 48; +static unsigned int const OUE_key_bytes_V5 = 32; +static unsigned int const Perms_key_bytes_V5 = 16; + +int +QPDF::EncryptionData::getV() const +{ + return this->V; +} + +int +QPDF::EncryptionData::getR() const +{ + return this->R; +} + +int +QPDF::EncryptionData::getLengthBytes() const +{ + return this->Length_bytes; +} + +int +QPDF::EncryptionData::getP() const +{ + return this->P; +} + +std::string const& +QPDF::EncryptionData::getO() const +{ + return this->O; +} + +std::string const& +QPDF::EncryptionData::getU() const +{ + return this->U; +} + +std::string const& +QPDF::EncryptionData::getOE() const +{ + return this->OE; +} + +std::string const& +QPDF::EncryptionData::getUE() const +{ + return this->UE; +} + +std::string const& +QPDF::EncryptionData::getPerms() const +{ + return this->Perms; +} + +std::string const& +QPDF::EncryptionData::getId1() const +{ + return this->id1; +} + +bool +QPDF::EncryptionData::getEncryptMetadata() const +{ + return this->encrypt_metadata; +} + void -pad_or_truncate_password(std::string const& password, char k1[key_bytes]) +QPDF::EncryptionData::setO(std::string const& O) +{ + this->O = O; +} + +void +QPDF::EncryptionData::setU(std::string const& U) +{ + this->U = U; +} + +void +QPDF::EncryptionData::setV5EncryptionParameters( + std::string const& O, + std::string const& OE, + std::string const& U, + std::string const& UE, + std::string const& Perms) +{ + this->O = O; + this->OE = OE; + this->U = U; + this->UE = UE; + this->Perms = Perms; +} + +static void +pad_or_truncate_password_V4(std::string const& password, char k1[key_bytes]) { int password_bytes = std::min(key_bytes, (unsigned int)password.length()); int pad_bytes = key_bytes - password_bytes; @@ -48,25 +148,37 @@ QPDF::trim_user_password(std::string& user_password) return; } - char const* p = 0; - while ((p = strchr(cstr, '\x28')) != 0) + char const* p1 = cstr; + char const* p2 = 0; + while ((p2 = strchr(p1, '\x28')) != 0) { - if (memcmp(p, padding_string, len - (p - cstr)) == 0) + if (memcmp(p2, padding_string, len - (p2 - cstr)) == 0) { - user_password = user_password.substr(0, p - cstr); + user_password = user_password.substr(0, p2 - cstr); return; } + else + { + QTC::TC("qpdf", "QPDF_encryption skip 0x28"); + p1 = p2 + 1; + } } } static std::string -pad_or_truncate_password(std::string const& password) +pad_or_truncate_password_V4(std::string const& password) { char k1[key_bytes]; - pad_or_truncate_password(password, k1); + pad_or_truncate_password_V4(password, k1); return std::string(k1, key_bytes); } +static std::string +truncate_password_V5(std::string const& password) +{ + return password.substr(0, std::min((size_t)127, password.length())); +} + static void iterate_md5_digest(MD5& md5, MD5::Digest& digest, int iterations) { @@ -100,15 +212,146 @@ iterate_rc4(unsigned char* data, int data_len, delete [] key; } +static std::string +process_with_aes(std::string const& key, + bool encrypt, + std::string const& data, + size_t outlength = 0, + unsigned int repetitions = 1, + unsigned char const* iv = 0, + size_t iv_length = 0) +{ + Pl_Buffer buffer("buffer"); + Pl_AES_PDF aes("aes", &buffer, encrypt, + (unsigned char const*)key.c_str(), + (unsigned int)key.length()); + if (iv) + { + aes.setIV(iv, iv_length); + } + else + { + aes.useZeroIV(); + } + aes.disablePadding(); + for (unsigned int i = 0; i < repetitions; ++i) + { + aes.write((unsigned char*)data.c_str(), data.length()); + } + aes.finish(); + PointerHolder bufp = buffer.getBuffer(); + if (outlength == 0) + { + outlength = bufp->getSize(); + } + else + { + outlength = std::min(outlength, bufp->getSize()); + } + return std::string((char const*)bufp->getBuffer(), outlength); +} + +static std::string +hash_V5(std::string const& password, + std::string const& salt, + std::string const& udata, + QPDF::EncryptionData const& data) +{ + Pl_SHA2 hash(256); + hash.write((unsigned char*)password.c_str(), password.length()); + hash.write((unsigned char*)salt.c_str(), salt.length()); + hash.write((unsigned char*)udata.c_str(), udata.length()); + hash.finish(); + std::string K = hash.getRawDigest(); + + std::string result; + if (data.getR() < 6) + { + result = K; + } + else + { + // Algorithm 2.B from ISO 32000-1 chapter 7: Computing a hash + + int round_number = 0; + bool done = false; + while (! done) + { + // The hash algorithm has us setting K initially to the R5 + // value and then repeating a series of steps 64 times + // before starting with the termination case testing. The + // wording of the specification is very unclear as to the + // exact number of times it should be run since the + // wording about whether the initial setup counts as round + // 0 or not is ambiguous. This code counts the initial + // setup (R5) value as round 0, which appears to be + // correct. This was determined to be correct by + // increasing or decreasing the number of rounds by 1 or 2 + // from this value and generating 20 test files. In this + // interpretation, all the test files worked with Adobe + // Reader X. In the other configurations, many of the + // files did not work, and we were accurately able to + // predict which files didn't work by looking at the + // conditions under which we terminated repetition. + + ++round_number; + std::string K1 = password + K + udata; + assert(K.length() >= 32); + std::string E = process_with_aes( + K.substr(0, 16), true, K1, 0, 64, + (unsigned char*)K.substr(16, 16).c_str(), 16); + + // E_mod_3 is supposed to be mod 3 of the first 16 bytes + // of E taken as as a (128-bit) big-endian number. Since + // (xy mod n) is equal to ((x mod n) + (y mod n)) mod n + // and since 256 mod n is 1, we can just take the sums of + // the the mod 3s of each byte to get the same result. + int E_mod_3 = 0; + for (unsigned int i = 0; i < 16; ++i) + { + E_mod_3 += (unsigned char)E[i]; + } + E_mod_3 %= 3; + int next_hash = ((E_mod_3 == 0) ? 256 : + (E_mod_3 == 1) ? 384 : + 512); + Pl_SHA2 hash(next_hash); + hash.write((unsigned char*)E.c_str(), E.length()); + hash.finish(); + K = hash.getRawDigest(); + + if (round_number >= 64) + { + unsigned int ch = (unsigned int)((unsigned char) *(E.rbegin())); + + if (ch <= (unsigned int)(round_number - 32)) + { + done = true; + } + } + } + result = K.substr(0, 32); + } + + return result; +} + std::string QPDF::compute_data_key(std::string const& encryption_key, - int objid, int generation, - bool use_aes) + int objid, int generation, bool use_aes, + int encryption_V, int encryption_R) { // Algorithm 3.1 from the PDF 1.7 Reference Manual std::string result = encryption_key; + if (encryption_V >= 5) + { + // Algorithm 3.1a (PDF 1.7 extension level 3): just use + // encryption key straight. + return result; + } + // Append low three bytes of object ID and low two bytes of generation result += (char) (objid & 0xff); result += (char) ((objid >> 8) & 0xff); @@ -132,36 +375,69 @@ std::string QPDF::compute_encryption_key( std::string const& password, EncryptionData const& data) { + if (data.getV() >= 5) + { + // For V >= 5, the encryption key is generated and stored in + // the file, encrypted separately with both user and owner + // passwords. + return recover_encryption_key_with_password(password, data); + } + else + { + // For V < 5, the encryption key is derived from the user + // password. + return compute_encryption_key_from_password(password, data); + } +} + +std::string +QPDF::compute_encryption_key_from_password( + std::string const& password, EncryptionData const& data) +{ // Algorithm 3.2 from the PDF 1.7 Reference Manual + // This code does not properly handle Unicode passwords. + // Passwords are supposed to be converted from OS codepage + // characters to PDFDocEncoding. Unicode passwords are supposed + // to be converted to OS codepage before converting to + // PDFDocEncoding. We instead require the password to be + // presented in its final form. + MD5 md5; md5.encodeDataIncrementally( - pad_or_truncate_password(password).c_str(), key_bytes); - md5.encodeDataIncrementally(data.O.c_str(), key_bytes); + pad_or_truncate_password_V4(password).c_str(), key_bytes); + md5.encodeDataIncrementally(data.getO().c_str(), key_bytes); char pbytes[4]; - pbytes[0] = (char) (data.P & 0xff); - pbytes[1] = (char) ((data.P >> 8) & 0xff); - pbytes[2] = (char) ((data.P >> 16) & 0xff); - pbytes[3] = (char) ((data.P >> 24) & 0xff); + int P = data.getP(); + pbytes[0] = (char) (P & 0xff); + pbytes[1] = (char) ((P >> 8) & 0xff); + pbytes[2] = (char) ((P >> 16) & 0xff); + pbytes[3] = (char) ((P >> 24) & 0xff); md5.encodeDataIncrementally(pbytes, 4); - md5.encodeDataIncrementally(data.id1.c_str(), (int)data.id1.length()); - if ((data.R >= 4) && (! data.encrypt_metadata)) + md5.encodeDataIncrementally(data.getId1().c_str(), + (int)data.getId1().length()); + if ((data.getR() >= 4) && (! data.getEncryptMetadata())) { char bytes[4]; memset(bytes, 0xff, 4); md5.encodeDataIncrementally(bytes, 4); } MD5::Digest digest; - iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0)); - return std::string((char*)digest, data.Length_bytes); + iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0)); + return std::string((char*)digest, data.getLengthBytes()); } static void compute_O_rc4_key(std::string const& user_password, std::string const& owner_password, QPDF::EncryptionData const& data, - unsigned char key[O_key_bytes]) + unsigned char key[OU_key_bytes_V4]) { + if (data.getV() >= 5) + { + throw std::logic_error( + "compute_O_rc4_key called for file with V >= 5"); + } std::string password = owner_password; if (password.empty()) { @@ -169,10 +445,10 @@ compute_O_rc4_key(std::string const& user_password, } MD5 md5; md5.encodeDataIncrementally( - pad_or_truncate_password(password).c_str(), key_bytes); + pad_or_truncate_password_V4(password).c_str(), key_bytes); MD5::Digest digest; - iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0)); - memcpy(key, digest, O_key_bytes); + iterate_md5_digest(md5, digest, ((data.getR() >= 3) ? 50 : 0)); + memcpy(key, digest, OU_key_bytes_V4); } static std::string @@ -182,13 +458,14 @@ compute_O_value(std::string const& user_password, { // Algorithm 3.3 from the PDF 1.7 Reference Manual - unsigned char O_key[O_key_bytes]; + unsigned char O_key[OU_key_bytes_V4]; compute_O_rc4_key(user_password, owner_password, data, O_key); char upass[key_bytes]; - pad_or_truncate_password(user_password, upass); + pad_or_truncate_password_V4(user_password, upass); iterate_rc4((unsigned char*) upass, key_bytes, - O_key, data.Length_bytes, (data.R >= 3) ? 20 : 1, false); + O_key, data.getLengthBytes(), + (data.getR() >= 3) ? 20 : 1, false); return std::string(upass, key_bytes); } @@ -201,9 +478,9 @@ compute_U_value_R2(std::string const& user_password, std::string k1 = QPDF::compute_encryption_key(user_password, data); char udata[key_bytes]; - pad_or_truncate_password("", udata); + pad_or_truncate_password_V4("", udata); iterate_rc4((unsigned char*) udata, key_bytes, - (unsigned char*)k1.c_str(), data.Length_bytes, 1, false); + (unsigned char*)k1.c_str(), data.getLengthBytes(), 1, false); return std::string(udata, key_bytes); } @@ -217,12 +494,13 @@ compute_U_value_R3(std::string const& user_password, std::string k1 = QPDF::compute_encryption_key(user_password, data); MD5 md5; md5.encodeDataIncrementally( - pad_or_truncate_password("").c_str(), key_bytes); - md5.encodeDataIncrementally(data.id1.c_str(), (int)data.id1.length()); + pad_or_truncate_password_V4("").c_str(), key_bytes); + md5.encodeDataIncrementally(data.getId1().c_str(), + (int)data.getId1().length()); MD5::Digest digest; md5.digest(digest); iterate_rc4(digest, sizeof(MD5::Digest), - (unsigned char*) k1.c_str(), data.Length_bytes, 20, false); + (unsigned char*) k1.c_str(), data.getLengthBytes(), 20, false); char result[key_bytes]; memcpy(result, digest, sizeof(MD5::Digest)); // pad with arbitrary data -- make it consistent for the sake of @@ -238,7 +516,7 @@ static std::string compute_U_value(std::string const& user_password, QPDF::EncryptionData const& data) { - if (data.R >= 3) + if (data.getR() >= 3) { return compute_U_value_R3(user_password, data); } @@ -247,40 +525,214 @@ compute_U_value(std::string const& user_password, } static bool -check_user_password(std::string const& user_password, - QPDF::EncryptionData const& data) +check_user_password_V4(std::string const& user_password, + QPDF::EncryptionData const& data) { // Algorithm 3.6 from the PDF 1.7 Reference Manual std::string u_value = compute_U_value(user_password, data); - int to_compare = ((data.R >= 3) ? sizeof(MD5::Digest) : key_bytes); - return (memcmp(data.U.c_str(), u_value.c_str(), to_compare) == 0); + int to_compare = ((data.getR() >= 3) ? sizeof(MD5::Digest) + : key_bytes); + return (memcmp(data.getU().c_str(), u_value.c_str(), to_compare) == 0); } static bool -check_owner_password(std::string& user_password, - std::string const& owner_password, - QPDF::EncryptionData const& data) +check_user_password_V5(std::string const& user_password, + QPDF::EncryptionData const& data) +{ + // Algorithm 3.11 from the PDF 1.7 extension level 3 + + std::string user_data = data.getU().substr(0, 32); + std::string validation_salt = data.getU().substr(32, 8); + std::string password = truncate_password_V5(user_password); + return (hash_V5(password, validation_salt, "", data) == user_data); +} + +static bool +check_user_password(std::string const& user_password, + QPDF::EncryptionData const& data) +{ + if (data.getV() < 5) + { + return check_user_password_V4(user_password, data); + } + else + { + return check_user_password_V5(user_password, data); + } +} + +static bool +check_owner_password_V4(std::string& user_password, + std::string const& owner_password, + QPDF::EncryptionData const& data) { // Algorithm 3.7 from the PDF 1.7 Reference Manual - unsigned char key[O_key_bytes]; + unsigned char key[OU_key_bytes_V4]; compute_O_rc4_key(user_password, owner_password, data, key); unsigned char O_data[key_bytes]; - memcpy(O_data, (unsigned char*) data.O.c_str(), key_bytes); - iterate_rc4(O_data, key_bytes, key, data.Length_bytes, - (data.R >= 3) ? 20 : 1, true); + memcpy(O_data, (unsigned char*) data.getO().c_str(), key_bytes); + iterate_rc4(O_data, key_bytes, key, data.getLengthBytes(), + (data.getR() >= 3) ? 20 : 1, true); std::string new_user_password = - std::string((char*)O_data, key_bytes); + std::string((char*)O_data, key_bytes); bool result = false; if (check_user_password(new_user_password, data)) { - result = true; - user_password = new_user_password; + result = true; + user_password = new_user_password; } return result; } +static bool +check_owner_password_V5(std::string const& owner_password, + QPDF::EncryptionData const& data) +{ + // Algorithm 3.12 from the PDF 1.7 extension level 3 + + std::string user_data = data.getU().substr(0, 48); + std::string owner_data = data.getO().substr(0, 32); + std::string validation_salt = data.getO().substr(32, 8); + std::string password = truncate_password_V5(owner_password); + return (hash_V5(password, validation_salt, user_data, + data) == owner_data); +} + +static bool +check_owner_password(std::string& user_password, + std::string const& owner_password, + QPDF::EncryptionData const& data) +{ + if (data.getV() < 5) + { + return check_owner_password_V4(user_password, owner_password, data); + } + else + { + return check_owner_password_V5(owner_password, data); + } +} + +std::string +QPDF::recover_encryption_key_with_password( + std::string const& password, EncryptionData const& data) +{ + // Disregard whether Perms is valid. + bool disregard; + return recover_encryption_key_with_password(password, data, disregard); +} + +static void +compute_U_UE_value_V5(std::string const& user_password, + std::string const& encryption_key, + QPDF::EncryptionData const& data, + std::string& U, std::string& UE) +{ + // Algorithm 3.8 from the PDF 1.7 extension level 3 + char k[16]; + QUtil::initializeWithRandomBytes((unsigned char*) k, sizeof(k)); + std::string validation_salt(k, 8); + std::string key_salt(k + 8, 8); + U = hash_V5(user_password, validation_salt, "", data) + + validation_salt + key_salt; + std::string intermediate_key = hash_V5(user_password, key_salt, "", data); + UE = process_with_aes(intermediate_key, true, encryption_key); +} + +static void +compute_O_OE_value_V5(std::string const& owner_password, + std::string const& encryption_key, + QPDF::EncryptionData const& data, + std::string const& U, + std::string& O, std::string& OE) +{ + // Algorithm 3.9 from the PDF 1.7 extension level 3 + char k[16]; + QUtil::initializeWithRandomBytes((unsigned char*) k, sizeof(k)); + std::string validation_salt(k, 8); + std::string key_salt(k + 8, 8); + O = hash_V5(owner_password, validation_salt, U, data) + + validation_salt + key_salt; + std::string intermediate_key = hash_V5(owner_password, key_salt, U, data); + OE = process_with_aes(intermediate_key, true, encryption_key); +} + +void +compute_Perms_value_V5_clear(std::string const& encryption_key, + QPDF::EncryptionData const& data, + unsigned char k[16]) +{ + // From algorithm 3.10 from the PDF 1.7 extension level 3 + unsigned long long extended_perms = 0xffffffff00000000LL | data.getP(); + for (int i = 0; i < 8; ++i) + { + k[i] = (unsigned char) (extended_perms & 0xff); + extended_perms >>= 8; + } + k[8] = data.getEncryptMetadata() ? 'T' : 'F'; + k[9] = 'a'; + k[10] = 'd'; + k[11] = 'b'; + QUtil::initializeWithRandomBytes(k + 12, 4); +} + +static std::string +compute_Perms_value_V5(std::string const& encryption_key, + QPDF::EncryptionData const& data) +{ + // Algorithm 3.10 from the PDF 1.7 extension level 3 + unsigned char k[16]; + compute_Perms_value_V5_clear(encryption_key, data, k); + return process_with_aes(encryption_key, true, + std::string((char const*) k, sizeof(k))); +} + +std::string +QPDF::recover_encryption_key_with_password( + std::string const& password, EncryptionData const& data, + bool& perms_valid) +{ + // Algorithm 3.2a from the PDF 1.7 extension level 3 + + // This code does not handle Unicode passwords correctly. + // Empirical evidence suggests that most viewers don't. We are + // supposed to process the input string with the SASLprep (RFC + // 4013) profile of stringprep (RFC 3454) and then convert the + // result to UTF-8. + + perms_valid = false; + std::string key_password = truncate_password_V5(password); + std::string key_salt; + std::string user_data; + std::string encrypted_file_key; + if (check_owner_password_V5(key_password, data)) + { + key_salt = data.getO().substr(40, 8); + user_data = data.getU().substr(0, 48); + encrypted_file_key = data.getOE().substr(0, 32); + } + else if (check_user_password_V5(key_password, data)) + { + key_salt = data.getU().substr(40, 8); + encrypted_file_key = data.getUE().substr(0, 32); + } + std::string intermediate_key = + hash_V5(key_password, key_salt, user_data, data); + std::string file_key = + process_with_aes(intermediate_key, false, encrypted_file_key); + + // Decrypt Perms and check against expected value + std::string perms_check = + process_with_aes(file_key, false, data.getPerms(), 12); + unsigned char k[16]; + compute_Perms_value_V5_clear(file_key, data, k); + perms_valid = (memcmp(perms_check.c_str(), k, 12) == 0); + + return file_key; +} + QPDF::encryption_method_e QPDF::interpretCF(QPDFObjectHandle cf) { @@ -384,29 +836,70 @@ QPDF::initializeEncryption() std::string U = encryption_dict.getKey("/U").getStringValue(); unsigned int P = (unsigned int) encryption_dict.getKey("/P").getIntValue(); - if (! (((R == 2) || (R == 3) || (R == 4)) && - ((V == 1) || (V == 2) || (V == 4)))) + // If supporting new encryption R/V values, remember to update + // error message inside this if statement. + if (! (((R >= 2) && (R <= 6)) && + ((V == 1) || (V == 2) || (V == 4) || (V == 5)))) { throw QPDFExc(qpdf_e_unsupported, this->file->getName(), "encryption dictionary", this->file->getLastOffset(), - "Unsupported /R or /V in encryption dictionary"); + "Unsupported /R or /V in encryption dictionary; R = " + + QUtil::int_to_string(R) + " (max 6), V = " + + QUtil::int_to_string(V) + " (max 5)"); } this->encryption_V = V; + this->encryption_R = R; - if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) + // OE, UE, and Perms are only present if V >= 5. + std::string OE; + std::string UE; + std::string Perms; + + if (V < 5) { - throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), - "encryption dictionary", this->file->getLastOffset(), - "incorrect length for /O and/or /P in " - "encryption dictionary"); + if (! ((O.length() == key_bytes) && (U.length() == key_bytes))) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "encryption dictionary", this->file->getLastOffset(), + "incorrect length for /O and/or /U in " + "encryption dictionary"); + } + } + else + { + if (! (encryption_dict.getKey("/OE").isString() && + encryption_dict.getKey("/UE").isString() && + encryption_dict.getKey("/Perms").isString())) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "encryption dictionary", this->file->getLastOffset(), + "some V=5 encryption dictionary parameters are " + "missing or the wrong type"); + } + OE = encryption_dict.getKey("/OE").getStringValue(); + UE = encryption_dict.getKey("/UE").getStringValue(); + Perms = encryption_dict.getKey("/Perms").getStringValue(); + + if ((O.length() < OU_key_bytes_V5) || + (U.length() < OU_key_bytes_V5) || + (OE.length() < OUE_key_bytes_V5) || + (UE.length() < OUE_key_bytes_V5) || + (Perms.length() < Perms_key_bytes_V5)) + { + throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "encryption dictionary", this->file->getLastOffset(), + "incorrect length for some of" + " /O, /U, /OE, /UE, or /Perms in" + " encryption dictionary"); + } } int Length = 40; if (encryption_dict.getKey("/Length").isInteger()) { Length = encryption_dict.getKey("/Length").getIntValue(); - if ((Length % 8) || (Length < 40) || (Length > 128)) + if ((Length % 8) || (Length < 40) || (Length > 256)) { throw QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), "encryption dictionary", this->file->getLastOffset(), @@ -421,7 +914,7 @@ QPDF::initializeEncryption() encryption_dict.getKey("/EncryptMetadata").getBoolValue(); } - if (V == 4) + if ((V == 4) || (V == 5)) { QPDFObjectHandle CF = encryption_dict.getKey("/CF"); std::set keys = CF.getKeys(); @@ -446,6 +939,11 @@ QPDF::initializeEncryption() QTC::TC("qpdf", "QPDF_encryption CFM AESV2"); method = e_aes; } + else if (method_name == "/AESV3") + { + QTC::TC("qpdf", "QPDF_encryption CFM AESV3"); + method = e_aesv3; + } else { // Don't complain now -- maybe we won't need @@ -470,35 +968,15 @@ QPDF::initializeEncryption() { this->cf_file = this->cf_stream; } - if (this->cf_file != this->cf_stream) - { - // The issue for qpdf is that it can't tell the difference - // between an embedded file stream and a regular stream. - // Search for a comment containing cf_file. To fix this, - // we need files with encrypted embedded files and - // non-encrypted native streams and vice versa. Also if - // it is possible for them to be encrypted in different - // ways, we should have some of those too. In cases where - // we can detect whether a stream is encrypted or not, we - // might want to try to detecet that automatically in - // defense of possible logic errors surrounding detection - // of embedded file streams, unless that's really clear - // from the specification. - throw QPDFExc(qpdf_e_unsupported, this->file->getName(), - "encryption dictionary", this->file->getLastOffset(), - "This document has embedded files that are" - " encrypted differently from the rest of the file." - " qpdf does not presently support this due to" - " lack of test data; if possible, please submit" - " a bug report that includes this file."); - } } - EncryptionData data(V, R, Length / 8, P, O, U, id1, this->encrypt_metadata); + + EncryptionData data(V, R, Length / 8, P, O, U, OE, UE, Perms, + id1, this->encrypt_metadata); if (check_owner_password( this->user_password, this->provided_password, data)) { // password supplied was owner password; user_password has - // been initialized + // been initialized for V < 5 } else if (check_user_password(this->provided_password, data)) { @@ -510,7 +988,30 @@ QPDF::initializeEncryption() "", 0, "invalid password"); } - this->encryption_key = compute_encryption_key(this->user_password, data); + if (V < 5) + { + // For V < 5, the user password is encrypted with the owner + // password, and the user password is always used for + // computing the encryption key. + this->encryption_key = compute_encryption_key( + this->user_password, data); + } + else + { + // For V >= 5, either password can be used independently to + // compute the encryption key, and neither password can be + // used to recover the other. + bool perms_valid; + this->encryption_key = recover_encryption_key_with_password( + this->provided_password, data, perms_valid); + if (! perms_valid) + { + warn(QPDFExc(qpdf_e_damaged_pdf, this->file->getName(), + "encryption dictionary", this->file->getLastOffset(), + "/Perms field in encryption dictionary" + " doesn't match expected value")); + } + } } std::string @@ -526,7 +1027,8 @@ QPDF::getKeyForObject(int objid, int generation, bool use_aes) (generation == this->cached_key_generation))) { this->cached_object_encryption_key = - compute_data_key(this->encryption_key, objid, generation, use_aes); + compute_data_key(this->encryption_key, objid, generation, + use_aes, this->encryption_V, this->encryption_R); this->cached_key_objid = objid; this->cached_key_generation = generation; } @@ -542,7 +1044,7 @@ QPDF::decryptString(std::string& str, int objid, int generation) return; } bool use_aes = false; - if (this->encryption_V == 4) + if (this->encryption_V >= 4) { switch (this->cf_string) { @@ -553,6 +1055,10 @@ QPDF::decryptString(std::string& str, int objid, int generation) use_aes = true; break; + case e_aesv3: + use_aes = true; + break; + case e_rc4: break; @@ -576,10 +1082,10 @@ QPDF::decryptString(std::string& str, int objid, int generation) if (use_aes) { QTC::TC("qpdf", "QPDF_encryption aes decode string"); - assert(key.length() == Pl_AES_PDF::key_size); Pl_Buffer bufpl("decrypted string"); Pl_AES_PDF pl("aes decrypt string", &bufpl, false, - (unsigned char const*)key.c_str()); + (unsigned char const*)key.c_str(), + (unsigned int)key.length()); pl.write((unsigned char*)str.c_str(), str.length()); pl.finish(); PointerHolder buf = bufpl.getBuffer(); @@ -628,23 +1134,53 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, return; } bool use_aes = false; - if (this->encryption_V == 4) + if (this->encryption_V >= 4) { encryption_method_e method = e_unknown; std::string method_source = "/StmF from /Encrypt dictionary"; - if (stream_dict.getKey("/Filter").isOrHasName("/Crypt") && - stream_dict.getKey("/DecodeParms").isDictionary()) - { - QPDFObjectHandle decode_parms = stream_dict.getKey("/DecodeParms"); - if (decode_parms.getKey("/Type").isName() && - (decode_parms.getKey("/Type").getName() == - "/CryptFilterDecodeParms")) - { - QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); - method = interpretCF(decode_parms.getKey("/Name")); - method_source = "stream's Crypt decode parameters"; - } + if (stream_dict.getKey("/Filter").isOrHasName("/Crypt")) + { + if (stream_dict.getKey("/DecodeParms").isDictionary()) + { + QPDFObjectHandle decode_parms = + stream_dict.getKey("/DecodeParms"); + if (decode_parms.getKey("/Type").isName() && + (decode_parms.getKey("/Type").getName() == + "/CryptFilterDecodeParms")) + { + QTC::TC("qpdf", "QPDF_encryption stream crypt filter"); + method = interpretCF(decode_parms.getKey("/Name")); + method_source = "stream's Crypt decode parameters"; + } + } + else if (stream_dict.getKey("/DecodeParms").isArray() && + stream_dict.getKey("/Filter").isArray()) + { + QPDFObjectHandle filter = stream_dict.getKey("/Filter"); + QPDFObjectHandle decode = stream_dict.getKey("/DecodeParms"); + if (filter.getArrayNItems() == decode.getArrayNItems()) + { + for (int i = 0; i < filter.getArrayNItems(); ++i) + { + if (filter.getArrayItem(i).isName() && + (filter.getArrayItem(i).getName() == "/Crypt")) + { + QPDFObjectHandle crypt_params = + decode.getArrayItem(i); + if (crypt_params.isDictionary() && + crypt_params.getKey("/Name").isName()) + { + QTC::TC("qpdf", "QPDF_encrypt crypt array"); + method = interpretCF( + crypt_params.getKey("/Name")); + method_source = "stream's Crypt " + "decode parameters (array)"; + } + } + } + } + } } if (method == e_unknown) @@ -656,12 +1192,15 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, } else { - // NOTE: We should should use cf_file if this is an - // embedded file, but we can't yet detect embedded - // file streams as such. When fixing, search for all - // occurrences of cf_file to find a reference to this - // comment. - method = this->cf_stream; + if (this->attachment_streams.count( + ObjGen(objid, generation)) > 0) + { + method = this->cf_file; + } + else + { + method = this->cf_stream; + } } } use_aes = false; @@ -675,6 +1214,10 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, use_aes = true; break; + case e_aesv3: + use_aes = true; + break; + case e_rc4: break; @@ -696,15 +1239,16 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation, if (use_aes) { QTC::TC("qpdf", "QPDF_encryption aes decode stream"); - assert(key.length() == Pl_AES_PDF::key_size); pipeline = new Pl_AES_PDF("AES stream decryption", pipeline, - false, (unsigned char*) key.c_str()); + false, (unsigned char*) key.c_str(), + (unsigned int) key.length()); } else { QTC::TC("qpdf", "QPDF_encryption rc4 decode stream"); pipeline = new Pl_RC4("RC4 stream decryption", pipeline, - (unsigned char*) key.c_str(), (int)key.length()); + (unsigned char*) key.c_str(), + (unsigned int) key.length()); } heap.push_back(pipeline); } @@ -715,10 +1259,37 @@ QPDF::compute_encryption_O_U( int V, int R, int key_len, int P, bool encrypt_metadata, std::string const& id1, std::string& O, std::string& U) { - EncryptionData data(V, R, key_len, P, "", "", id1, encrypt_metadata); - data.O = compute_O_value(user_password, owner_password, data); - O = data.O; - U = compute_U_value(user_password, data); + if (V >= 5) + { + throw std::logic_error( + "compute_encryption_O_U called for file with V >= 5"); + } + EncryptionData data(V, R, key_len, P, "", "", "", "", "", + id1, encrypt_metadata); + data.setO(compute_O_value(user_password, owner_password, data)); + O = data.getO(); + data.setU(compute_U_value(user_password, data)); + U = data.getU(); +} + +void +QPDF::compute_encryption_parameters_V5( + char const* user_password, char const* owner_password, + int V, int R, int key_len, int P, bool encrypt_metadata, + std::string const& id1, + std::string& encryption_key, + std::string& O, std::string& U, + std::string& OE, std::string& UE, std::string& Perms) +{ + EncryptionData data(V, R, key_len, P, "", "", "", "", "", + id1, encrypt_metadata); + unsigned char k[key_bytes]; + QUtil::initializeWithRandomBytes(k, key_bytes); + encryption_key = std::string((char const*)k, key_bytes); + compute_U_UE_value_V5(user_password, encryption_key, data, U, UE); + compute_O_OE_value_V5(owner_password, encryption_key, data, U, O, OE); + Perms = compute_Perms_value_V5(encryption_key, data); + data.setV5EncryptionParameters(O, OE, U, UE, Perms); } std::string const& @@ -735,6 +1306,12 @@ QPDF::getTrimmedUserPassword() const return result; } +std::string +QPDF::getEncryptionKey() const +{ + return this->encryption_key; +} + bool QPDF::isEncrypted() const { diff --git a/libqpdf/QPDF_optimization.cc b/libqpdf/QPDF_optimization.cc index 67f147f..f832a88 100644 --- a/libqpdf/QPDF_optimization.cc +++ b/libqpdf/QPDF_optimization.cc @@ -59,103 +59,6 @@ QPDF::ObjUser::operator<(ObjUser const& rhs) const } void -QPDF::flattenScalarReferences() -{ - // Do a traversal of the entire PDF file structure replacing all - // indirect objects that are not arrays, streams, or dictionaries - // with direct objects. - - std::list queue; - queue.push_back(this->trailer); - std::set visited; - - // Add every object in the xref table to the queue. This ensures - // that we flatten scalar references in unreferenced objects. - // This becomes important if we are preserving object streams in a - // file that has unreferenced objects in its object streams. (See - // QPDF bug 2974522 at SourceForge.) - for (std::map::iterator iter = - this->xref_table.begin(); - iter != this->xref_table.end(); ++iter) - { - ObjGen const& og = (*iter).first; - queue.push_back(getObjectByID(og.obj, og.gen)); - } - - while (! queue.empty()) - { - QPDFObjectHandle node = queue.front(); - queue.pop_front(); - if (node.isIndirect()) - { - ObjGen og(node.getObjectID(), node.getGeneration()); - if (visited.count(og) > 0) - { - continue; - } - visited.insert(og); - } - - if (node.isArray()) - { - int nitems = node.getArrayNItems(); - for (int i = 0; i < nitems; ++i) - { - QPDFObjectHandle oh = node.getArrayItem(i); - if (oh.isScalar()) - { - if (oh.isIndirect()) - { - QTC::TC("qpdf", "QPDF opt flatten array scalar"); - oh.makeDirect(); - node.setArrayItem(i, oh); - } - } - else - { - queue.push_back(oh); - } - } - } - else if (node.isDictionary() || node.isStream()) - { - QPDFObjectHandle dict = node; - if (node.isStream()) - { - dict = node.getDict(); - } - std::set keys = dict.getKeys(); - for (std::set::iterator iter = keys.begin(); - iter != keys.end(); ++iter) - { - std::string const& key = *iter; - QPDFObjectHandle oh = dict.getKey(key); - if (oh.isNull()) - { - // QPDF_Dictionary.getKeys() never returns null - // keys. - throw std::logic_error( - "INTERNAL ERROR: dictionary with null key found"); - } - else if (oh.isScalar()) - { - if (oh.isIndirect()) - { - QTC::TC("qpdf", "QPDF opt flatten dict scalar"); - oh.makeDirect(); - dict.replaceKey(key, oh); - } - } - else - { - queue.push_back(oh); - } - } - } - } -} - -void QPDF::optimize(std::map const& object_stream_data, bool allow_changes) { @@ -304,9 +207,7 @@ QPDF::pushInheritedAttributesToPageInternal( } else { - // Don't defeat flattenScalarReferences which - // would have already been called by this - // time. + // It's okay to copy scalars. QTC::TC("qpdf", "QPDF opt inherited scalar"); } } diff --git a/libqpdf/QUtil.cc b/libqpdf/QUtil.cc index 2014308..3cdfdc4 100644 --- a/libqpdf/QUtil.cc +++ b/libqpdf/QUtil.cc @@ -333,3 +333,42 @@ QUtil::toUTF8(unsigned long uval) return result; } + +long +QUtil::random() +{ + static bool seeded_random = false; + if (! seeded_random) + { + // Seed the random number generator with something simple, but + // just to be interesting, don't use the unmodified current + // time.... + QUtil::srandom((int)QUtil::get_current_time() ^ 0xcccc); + seeded_random = true; + } + +#ifdef HAVE_RANDOM + return ::random(); +#else + return rand(); +#endif +} + +void +QUtil::srandom(unsigned int seed) +{ +#ifdef HAVE_RANDOM + ::srandom(seed); +#else + srand(seed); +#endif +} + +void +QUtil::initializeWithRandomBytes(unsigned char* data, size_t len) +{ + for (size_t i = 0; i < len; ++i) + { + data[i] = (unsigned char)((QUtil::random() & 0xff0) >> 4); + } +} diff --git a/libqpdf/build.mk b/libqpdf/build.mk index 6debf10..d64fde5 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -12,6 +12,7 @@ SRCS_libqpdf = \ libqpdf/FileInputSource.cc \ libqpdf/InputSource.cc \ libqpdf/MD5.cc \ + libqpdf/OffsetInputSource.cc \ libqpdf/PCRE.cc \ libqpdf/Pipeline.cc \ libqpdf/Pl_AES_PDF.cc \ @@ -27,6 +28,7 @@ SRCS_libqpdf = \ libqpdf/Pl_PNGFilter.cc \ libqpdf/Pl_QPDFTokenizer.cc \ libqpdf/Pl_RC4.cc \ + libqpdf/Pl_SHA2.cc \ libqpdf/Pl_StdioFile.cc \ libqpdf/QPDF.cc \ libqpdf/QPDFExc.cc \ @@ -53,18 +55,27 @@ SRCS_libqpdf = \ libqpdf/QUtil.cc \ libqpdf/RC4.cc \ libqpdf/qpdf-c.cc \ - libqpdf/rijndael.cc + libqpdf/rijndael.cc \ + libqpdf/sha2.c \ + libqpdf/sha2big.c # ----- -OBJS_libqpdf = $(call src_to_lobj,$(SRCS_libqpdf)) +CCSRCS_libqpdf = $(filter %.cc,$(SRCS_libqpdf)) +CSRCS_libqpdf = $(filter %.c,$(SRCS_libqpdf)) + +CCOBJS_libqpdf = $(call src_to_lobj,$(CCSRCS_libqpdf)) +COBJS_libqpdf = $(call c_src_to_lobj,$(CSRCS_libqpdf)) +OBJS_libqpdf = $(CCOBJS_libqpdf) $(COBJS_libqpdf) ifeq ($(GENDEPS),1) -include $(call lobj_to_dep,$(OBJS_libqpdf)) endif -$(OBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.cc +$(CCOBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.cc $(call libcompile,$<,$(INCLUDES_libqpdf)) +$(COBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.c + $(call c_libcompile,$<,$(INCLUDES_libqpdf)) # Last three arguments to makelib are CURRENT,REVISION,AGE. # @@ -80,4 +91,4 @@ $(OBJS_libqpdf): libqpdf/$(OUTPUT_DIR)/%.$(LOBJ): libqpdf/%.cc # * Otherwise, increment REVISION $(TARGETS_libqpdf): $(OBJS_libqpdf) - $(call makelib,$(OBJS_libqpdf),$@,$(LDFLAGS),$(LIBS),9,0,1) + $(call makelib,$(OBJS_libqpdf),$@,$(LDFLAGS),$(LIBS),10,0,0) diff --git a/libqpdf/qpdf-c.cc b/libqpdf/qpdf-c.cc index 0312ae5..a46df63 100644 --- a/libqpdf/qpdf-c.cc +++ b/libqpdf/qpdf-c.cc @@ -288,6 +288,12 @@ char const* qpdf_get_pdf_version(qpdf_data qpdf) return qpdf->tmp_string.c_str(); } +int qpdf_get_pdf_extension_level(qpdf_data qpdf) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_get_pdf_extension_level"); + return qpdf->qpdf->getExtensionLevel(); +} + char const* qpdf_get_user_password(qpdf_data qpdf) { QTC::TC("qpdf", "qpdf-c called qpdf_get_user_password"); @@ -566,6 +572,32 @@ void qpdf_set_r4_encryption_parameters( encrypt_metadata, use_aes); } +void qpdf_set_r5_encryption_parameters( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + QPDF_BOOL encrypt_metadata) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_set_r5_encryption_parameters"); + qpdf->qpdf_writer->setR5EncryptionParameters( + user_password, owner_password, + allow_accessibility, allow_extract, print, modify, + encrypt_metadata); +} + +void qpdf_set_r6_encryption_parameters( + qpdf_data qpdf, char const* user_password, char const* owner_password, + QPDF_BOOL allow_accessibility, QPDF_BOOL allow_extract, + qpdf_r3_print_e print, qpdf_r3_modify_e modify, + QPDF_BOOL encrypt_metadata) +{ + QTC::TC("qpdf", "qpdf-c called qpdf_set_r6_encryption_parameters"); + qpdf->qpdf_writer->setR6EncryptionParameters( + user_password, owner_password, + allow_accessibility, allow_extract, print, modify, + encrypt_metadata); +} + void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value) { QTC::TC("qpdf", "qpdf-c called qpdf_set_linearization"); @@ -574,14 +606,26 @@ void qpdf_set_linearization(qpdf_data qpdf, QPDF_BOOL value) void qpdf_set_minimum_pdf_version(qpdf_data qpdf, char const* version) { + qpdf_set_minimum_pdf_version_and_extension(qpdf, version, 0); +} + +void qpdf_set_minimum_pdf_version_and_extension( + qpdf_data qpdf, char const* version, int extension_level) +{ QTC::TC("qpdf", "qpdf-c called qpdf_set_minimum_pdf_version"); - qpdf->qpdf_writer->setMinimumPDFVersion(version); + qpdf->qpdf_writer->setMinimumPDFVersion(version, extension_level); } void qpdf_force_pdf_version(qpdf_data qpdf, char const* version) { + qpdf_force_pdf_version_and_extension(qpdf, version, 0); +} + +void qpdf_force_pdf_version_and_extension( + qpdf_data qpdf, char const* version, int extension_level) +{ QTC::TC("qpdf", "qpdf-c called qpdf_force_pdf_version"); - qpdf->qpdf_writer->forcePDFVersion(version); + qpdf->qpdf_writer->forcePDFVersion(version, extension_level); } QPDF_ERROR_CODE qpdf_write(qpdf_data qpdf) diff --git a/libqpdf/qpdf/OffsetInputSource.hh b/libqpdf/qpdf/OffsetInputSource.hh new file mode 100644 index 0000000..aedc574 --- /dev/null +++ b/libqpdf/qpdf/OffsetInputSource.hh @@ -0,0 +1,29 @@ +#ifndef __QPDF_OFFSETINPUTSOURCE_HH__ +#define __QPDF_OFFSETINPUTSOURCE_HH__ + +// This class implements an InputSource that proxies for an underlying +// input source but offset a specific number of bytes. + +#include +#include + +class OffsetInputSource: public InputSource +{ + public: + OffsetInputSource(PointerHolder, qpdf_offset_t global_offset); + virtual ~OffsetInputSource(); + + virtual qpdf_offset_t findAndSkipNextEOL(); + virtual std::string const& getName() const; + virtual qpdf_offset_t tell(); + virtual void seek(qpdf_offset_t offset, int whence); + virtual void rewind(); + virtual size_t read(char* buffer, size_t length); + virtual void unreadCh(char ch); + + private: + PointerHolder proxied; + qpdf_offset_t global_offset; +}; + +#endif // __QPDF_OFFSETINPUTSOURCE_HH__ diff --git a/libqpdf/qpdf/Pl_AES_PDF.hh b/libqpdf/qpdf/Pl_AES_PDF.hh index 3947506..9aa73ad 100644 --- a/libqpdf/qpdf/Pl_AES_PDF.hh +++ b/libqpdf/qpdf/Pl_AES_PDF.hh @@ -7,17 +7,16 @@ # include #endif -// This pipeline implements AES-128 with CBC and block padding as -// specified in the PDF specification. +// This pipeline implements AES-128 and AES-256 with CBC and block +// padding as specified in the PDF specification. class Pl_AES_PDF: public Pipeline { public: - // key_data should be a pointer to key_size bytes of data - static unsigned int const key_size = 16; QPDF_DLL + // key should be a pointer to key_bytes bytes of data Pl_AES_PDF(char const* identifier, Pipeline* next, - bool encrypt, unsigned char const key[key_size]); + bool encrypt, unsigned char const* key, unsigned int key_bytes); QPDF_DLL virtual ~Pl_AES_PDF(); @@ -26,6 +25,17 @@ class Pl_AES_PDF: public Pipeline QPDF_DLL virtual void finish(); + // Use zero initialization vector; needed for AESV3 + QPDF_DLL + void useZeroIV(); + // Disable padding; needed for AESV3 + QPDF_DLL + void disablePadding(); + // Specify an initialization vector, which will not be included in + // the output. + QPDF_DLL + void setIV(unsigned char const* iv, size_t bytes); + // For testing only; PDF always uses CBC QPDF_DLL void disableCBC(); @@ -44,12 +54,16 @@ class Pl_AES_PDF: public Pipeline bool cbc_mode; bool first; size_t offset; // offset into memory buffer - unsigned char key[key_size]; - uint32_t rk[key_size + 28]; + unsigned char* key; + uint32_t* rk; unsigned char inbuf[buf_size]; unsigned char outbuf[buf_size]; unsigned char cbc_block[buf_size]; + unsigned char specified_iv[buf_size]; unsigned int nrounds; + bool use_zero_iv; + bool use_specified_iv; + bool disable_padding; }; #endif // __PL_AES_PDF_HH__ diff --git a/libqpdf/qpdf/Pl_SHA2.hh b/libqpdf/qpdf/Pl_SHA2.hh new file mode 100644 index 0000000..8ff4723 --- /dev/null +++ b/libqpdf/qpdf/Pl_SHA2.hh @@ -0,0 +1,50 @@ +#ifndef __PL_SHA2_HH__ +#define __PL_SHA2_HH__ + +// Bits must be a supported number of bits, currently only 256, 384, +// or 512. Passing 0 as bits leaves the pipeline uncommitted, in +// which case resetBits must be called before the pipeline is used. +// If a next is provided, this pipeline sends its output to its +// successor unmodified. After calling finish, the SHA2 checksum of +// the data that passed through the pipeline is available. + +// This pipeline is reusable; i.e., it is safe to call write() after +// calling finish(). The first call to write() after a call to +// finish() initializes a new SHA2 object. resetBits may also be +// called between finish and the next call to write. + +#include +#include + +class Pl_SHA2: public Pipeline +{ + public: + QPDF_DLL + Pl_SHA2(int bits = 0, Pipeline* next = 0); + QPDF_DLL + virtual ~Pl_SHA2(); + QPDF_DLL + virtual void write(unsigned char*, size_t); + QPDF_DLL + virtual void finish(); + QPDF_DLL + void resetBits(int bits); + QPDF_DLL + std::string getHexDigest(); + QPDF_DLL + std::string getRawDigest(); + + private: + void badBits(); + + bool in_progress; + int bits; + sph_sha256_context ctx256; + sph_sha384_context ctx384; + sph_sha512_context ctx512; + unsigned char sha256sum[32]; + unsigned char sha384sum[48]; + unsigned char sha512sum[64]; +}; + +#endif // __PL_SHA2_HH__ diff --git a/libqpdf/qpdf/QPDF_Stream.hh b/libqpdf/qpdf/QPDF_Stream.hh index 34eacee..6e5dacf 100644 --- a/libqpdf/qpdf/QPDF_Stream.hh +++ b/libqpdf/qpdf/QPDF_Stream.hh @@ -45,6 +45,9 @@ class QPDF_Stream: public QPDFObject void replaceFilterData(QPDFObjectHandle const& filter, QPDFObjectHandle const& decode_parms, size_t length); + bool understandDecodeParams( + std::string const& filter, QPDFObjectHandle decode_params, + int& predictor, int& columns, bool& early_code_change); bool filterable(std::vector& filters, int& predictor, int& columns, bool& early_code_change); diff --git a/libqpdf/sha2.c b/libqpdf/sha2.c new file mode 100644 index 0000000..45fdd7e --- /dev/null +++ b/libqpdf/sha2.c @@ -0,0 +1,690 @@ +/* $Id: sha2.c 227 2010-06-16 17:28:38Z tp $ */ +/* + * SHA-224 / SHA-256 implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph/sph_sha2.h" + +#if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_SHA2 +#define SPH_SMALL_FOOTPRINT_SHA2 1 +#endif + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X))) + +#define ROTR SPH_ROTR32 + +#define BSG2_0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define BSG2_1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SSG2_0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SPH_T32((x) >> 3)) +#define SSG2_1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SPH_T32((x) >> 10)) + +static const sph_u32 H224[8] = { + SPH_C32(0xC1059ED8), SPH_C32(0x367CD507), SPH_C32(0x3070DD17), + SPH_C32(0xF70E5939), SPH_C32(0xFFC00B31), SPH_C32(0x68581511), + SPH_C32(0x64F98FA7), SPH_C32(0xBEFA4FA4) +}; + +static const sph_u32 H256[8] = { + SPH_C32(0x6A09E667), SPH_C32(0xBB67AE85), SPH_C32(0x3C6EF372), + SPH_C32(0xA54FF53A), SPH_C32(0x510E527F), SPH_C32(0x9B05688C), + SPH_C32(0x1F83D9AB), SPH_C32(0x5BE0CD19) +}; + +/* + * The SHA2_ROUND_BODY defines the body for a SHA-224 / SHA-256 + * compression function implementation. The "in" parameter should + * evaluate, when applied to a numerical input parameter from 0 to 15, + * to an expression which yields the corresponding input block. The "r" + * parameter should evaluate to an array or pointer expression + * designating the array of 8 words which contains the input and output + * of the compression function. + */ + +#if SPH_SMALL_FOOTPRINT_SHA2 + +static const sph_u32 K[64] = { + SPH_C32(0x428A2F98), SPH_C32(0x71374491), + SPH_C32(0xB5C0FBCF), SPH_C32(0xE9B5DBA5), + SPH_C32(0x3956C25B), SPH_C32(0x59F111F1), + SPH_C32(0x923F82A4), SPH_C32(0xAB1C5ED5), + SPH_C32(0xD807AA98), SPH_C32(0x12835B01), + SPH_C32(0x243185BE), SPH_C32(0x550C7DC3), + SPH_C32(0x72BE5D74), SPH_C32(0x80DEB1FE), + SPH_C32(0x9BDC06A7), SPH_C32(0xC19BF174), + SPH_C32(0xE49B69C1), SPH_C32(0xEFBE4786), + SPH_C32(0x0FC19DC6), SPH_C32(0x240CA1CC), + SPH_C32(0x2DE92C6F), SPH_C32(0x4A7484AA), + SPH_C32(0x5CB0A9DC), SPH_C32(0x76F988DA), + SPH_C32(0x983E5152), SPH_C32(0xA831C66D), + SPH_C32(0xB00327C8), SPH_C32(0xBF597FC7), + SPH_C32(0xC6E00BF3), SPH_C32(0xD5A79147), + SPH_C32(0x06CA6351), SPH_C32(0x14292967), + SPH_C32(0x27B70A85), SPH_C32(0x2E1B2138), + SPH_C32(0x4D2C6DFC), SPH_C32(0x53380D13), + SPH_C32(0x650A7354), SPH_C32(0x766A0ABB), + SPH_C32(0x81C2C92E), SPH_C32(0x92722C85), + SPH_C32(0xA2BFE8A1), SPH_C32(0xA81A664B), + SPH_C32(0xC24B8B70), SPH_C32(0xC76C51A3), + SPH_C32(0xD192E819), SPH_C32(0xD6990624), + SPH_C32(0xF40E3585), SPH_C32(0x106AA070), + SPH_C32(0x19A4C116), SPH_C32(0x1E376C08), + SPH_C32(0x2748774C), SPH_C32(0x34B0BCB5), + SPH_C32(0x391C0CB3), SPH_C32(0x4ED8AA4A), + SPH_C32(0x5B9CCA4F), SPH_C32(0x682E6FF3), + SPH_C32(0x748F82EE), SPH_C32(0x78A5636F), + SPH_C32(0x84C87814), SPH_C32(0x8CC70208), + SPH_C32(0x90BEFFFA), SPH_C32(0xA4506CEB), + SPH_C32(0xBEF9A3F7), SPH_C32(0xC67178F2) +}; + +#define SHA2_MEXP1(in, pc) do { \ + W[pc] = in(pc); \ + } while (0) + +#define SHA2_MEXP2(in, pc) do { \ + W[(pc) & 0x0F] = SPH_T32(SSG2_1(W[((pc) - 2) & 0x0F]) \ + + W[((pc) - 7) & 0x0F] \ + + SSG2_0(W[((pc) - 15) & 0x0F]) + W[(pc) & 0x0F]); \ + } while (0) + +#define SHA2_STEPn(n, a, b, c, d, e, f, g, h, in, pc) do { \ + sph_u32 t1, t2; \ + SHA2_MEXP ## n(in, pc); \ + t1 = SPH_T32(h + BSG2_1(e) + CH(e, f, g) \ + + K[pcount + (pc)] + W[(pc) & 0x0F]); \ + t2 = SPH_T32(BSG2_0(a) + MAJ(a, b, c)); \ + d = SPH_T32(d + t1); \ + h = SPH_T32(t1 + t2); \ + } while (0) + +#define SHA2_STEP1(a, b, c, d, e, f, g, h, in, pc) \ + SHA2_STEPn(1, a, b, c, d, e, f, g, h, in, pc) +#define SHA2_STEP2(a, b, c, d, e, f, g, h, in, pc) \ + SHA2_STEPn(2, a, b, c, d, e, f, g, h, in, pc) + +#define SHA2_ROUND_BODY(in, r) do { \ + sph_u32 A, B, C, D, E, F, G, H; \ + sph_u32 W[16]; \ + unsigned pcount; \ + \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + pcount = 0; \ + SHA2_STEP1(A, B, C, D, E, F, G, H, in, 0); \ + SHA2_STEP1(H, A, B, C, D, E, F, G, in, 1); \ + SHA2_STEP1(G, H, A, B, C, D, E, F, in, 2); \ + SHA2_STEP1(F, G, H, A, B, C, D, E, in, 3); \ + SHA2_STEP1(E, F, G, H, A, B, C, D, in, 4); \ + SHA2_STEP1(D, E, F, G, H, A, B, C, in, 5); \ + SHA2_STEP1(C, D, E, F, G, H, A, B, in, 6); \ + SHA2_STEP1(B, C, D, E, F, G, H, A, in, 7); \ + SHA2_STEP1(A, B, C, D, E, F, G, H, in, 8); \ + SHA2_STEP1(H, A, B, C, D, E, F, G, in, 9); \ + SHA2_STEP1(G, H, A, B, C, D, E, F, in, 10); \ + SHA2_STEP1(F, G, H, A, B, C, D, E, in, 11); \ + SHA2_STEP1(E, F, G, H, A, B, C, D, in, 12); \ + SHA2_STEP1(D, E, F, G, H, A, B, C, in, 13); \ + SHA2_STEP1(C, D, E, F, G, H, A, B, in, 14); \ + SHA2_STEP1(B, C, D, E, F, G, H, A, in, 15); \ + for (pcount = 16; pcount < 64; pcount += 16) { \ + SHA2_STEP2(A, B, C, D, E, F, G, H, in, 0); \ + SHA2_STEP2(H, A, B, C, D, E, F, G, in, 1); \ + SHA2_STEP2(G, H, A, B, C, D, E, F, in, 2); \ + SHA2_STEP2(F, G, H, A, B, C, D, E, in, 3); \ + SHA2_STEP2(E, F, G, H, A, B, C, D, in, 4); \ + SHA2_STEP2(D, E, F, G, H, A, B, C, in, 5); \ + SHA2_STEP2(C, D, E, F, G, H, A, B, in, 6); \ + SHA2_STEP2(B, C, D, E, F, G, H, A, in, 7); \ + SHA2_STEP2(A, B, C, D, E, F, G, H, in, 8); \ + SHA2_STEP2(H, A, B, C, D, E, F, G, in, 9); \ + SHA2_STEP2(G, H, A, B, C, D, E, F, in, 10); \ + SHA2_STEP2(F, G, H, A, B, C, D, E, in, 11); \ + SHA2_STEP2(E, F, G, H, A, B, C, D, in, 12); \ + SHA2_STEP2(D, E, F, G, H, A, B, C, in, 13); \ + SHA2_STEP2(C, D, E, F, G, H, A, B, in, 14); \ + SHA2_STEP2(B, C, D, E, F, G, H, A, in, 15); \ + } \ + (r)[0] = SPH_T32((r)[0] + A); \ + (r)[1] = SPH_T32((r)[1] + B); \ + (r)[2] = SPH_T32((r)[2] + C); \ + (r)[3] = SPH_T32((r)[3] + D); \ + (r)[4] = SPH_T32((r)[4] + E); \ + (r)[5] = SPH_T32((r)[5] + F); \ + (r)[6] = SPH_T32((r)[6] + G); \ + (r)[7] = SPH_T32((r)[7] + H); \ + } while (0) + +#else + +#define SHA2_ROUND_BODY(in, r) do { \ + sph_u32 A, B, C, D, E, F, G, H, T1, T2; \ + sph_u32 W00, W01, W02, W03, W04, W05, W06, W07; \ + sph_u32 W08, W09, W10, W11, W12, W13, W14, W15; \ + \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + W00 = in(0); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x428A2F98) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = in(1); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x71374491) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = in(2); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xB5C0FBCF) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = in(3); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xE9B5DBA5) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = in(4); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x3956C25B) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = in(5); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x59F111F1) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = in(6); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x923F82A4) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = in(7); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xAB1C5ED5) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = in(8); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xD807AA98) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = in(9); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x12835B01) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = in(10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x243185BE) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = in(11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x550C7DC3) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = in(12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x72BE5D74) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = in(13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x80DEB1FE) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = in(14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x9BDC06A7) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = in(15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xC19BF174) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xE49B69C1) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xEFBE4786) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x0FC19DC6) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x240CA1CC) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x2DE92C6F) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x4A7484AA) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x5CB0A9DC) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x76F988DA) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x983E5152) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xA831C66D) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xB00327C8) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xBF597FC7) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0xC6E00BF3) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xD5A79147) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x06CA6351) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x14292967) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x27B70A85) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x2E1B2138) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x4D2C6DFC) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x53380D13) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x650A7354) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x766A0ABB) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x81C2C92E) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x92722C85) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0xA2BFE8A1) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0xA81A664B) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0xC24B8B70) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0xC76C51A3) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0xD192E819) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xD6990624) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0xF40E3585) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x106AA070) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W00 = SPH_T32(SSG2_1(W14) + W09 + SSG2_0(W01) + W00); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x19A4C116) + W00); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W01 = SPH_T32(SSG2_1(W15) + W10 + SSG2_0(W02) + W01); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x1E376C08) + W01); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W02 = SPH_T32(SSG2_1(W00) + W11 + SSG2_0(W03) + W02); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x2748774C) + W02); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W03 = SPH_T32(SSG2_1(W01) + W12 + SSG2_0(W04) + W03); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x34B0BCB5) + W03); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W04 = SPH_T32(SSG2_1(W02) + W13 + SSG2_0(W05) + W04); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x391C0CB3) + W04); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W05 = SPH_T32(SSG2_1(W03) + W14 + SSG2_0(W06) + W05); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0x4ED8AA4A) + W05); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W06 = SPH_T32(SSG2_1(W04) + W15 + SSG2_0(W07) + W06); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0x5B9CCA4F) + W06); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W07 = SPH_T32(SSG2_1(W05) + W00 + SSG2_0(W08) + W07); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0x682E6FF3) + W07); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + W08 = SPH_T32(SSG2_1(W06) + W01 + SSG2_0(W09) + W08); \ + T1 = SPH_T32(H + BSG2_1(E) + CH(E, F, G) \ + + SPH_C32(0x748F82EE) + W08); \ + T2 = SPH_T32(BSG2_0(A) + MAJ(A, B, C)); \ + D = SPH_T32(D + T1); \ + H = SPH_T32(T1 + T2); \ + W09 = SPH_T32(SSG2_1(W07) + W02 + SSG2_0(W10) + W09); \ + T1 = SPH_T32(G + BSG2_1(D) + CH(D, E, F) \ + + SPH_C32(0x78A5636F) + W09); \ + T2 = SPH_T32(BSG2_0(H) + MAJ(H, A, B)); \ + C = SPH_T32(C + T1); \ + G = SPH_T32(T1 + T2); \ + W10 = SPH_T32(SSG2_1(W08) + W03 + SSG2_0(W11) + W10); \ + T1 = SPH_T32(F + BSG2_1(C) + CH(C, D, E) \ + + SPH_C32(0x84C87814) + W10); \ + T2 = SPH_T32(BSG2_0(G) + MAJ(G, H, A)); \ + B = SPH_T32(B + T1); \ + F = SPH_T32(T1 + T2); \ + W11 = SPH_T32(SSG2_1(W09) + W04 + SSG2_0(W12) + W11); \ + T1 = SPH_T32(E + BSG2_1(B) + CH(B, C, D) \ + + SPH_C32(0x8CC70208) + W11); \ + T2 = SPH_T32(BSG2_0(F) + MAJ(F, G, H)); \ + A = SPH_T32(A + T1); \ + E = SPH_T32(T1 + T2); \ + W12 = SPH_T32(SSG2_1(W10) + W05 + SSG2_0(W13) + W12); \ + T1 = SPH_T32(D + BSG2_1(A) + CH(A, B, C) \ + + SPH_C32(0x90BEFFFA) + W12); \ + T2 = SPH_T32(BSG2_0(E) + MAJ(E, F, G)); \ + H = SPH_T32(H + T1); \ + D = SPH_T32(T1 + T2); \ + W13 = SPH_T32(SSG2_1(W11) + W06 + SSG2_0(W14) + W13); \ + T1 = SPH_T32(C + BSG2_1(H) + CH(H, A, B) \ + + SPH_C32(0xA4506CEB) + W13); \ + T2 = SPH_T32(BSG2_0(D) + MAJ(D, E, F)); \ + G = SPH_T32(G + T1); \ + C = SPH_T32(T1 + T2); \ + W14 = SPH_T32(SSG2_1(W12) + W07 + SSG2_0(W15) + W14); \ + T1 = SPH_T32(B + BSG2_1(G) + CH(G, H, A) \ + + SPH_C32(0xBEF9A3F7) + W14); \ + T2 = SPH_T32(BSG2_0(C) + MAJ(C, D, E)); \ + F = SPH_T32(F + T1); \ + B = SPH_T32(T1 + T2); \ + W15 = SPH_T32(SSG2_1(W13) + W08 + SSG2_0(W00) + W15); \ + T1 = SPH_T32(A + BSG2_1(F) + CH(F, G, H) \ + + SPH_C32(0xC67178F2) + W15); \ + T2 = SPH_T32(BSG2_0(B) + MAJ(B, C, D)); \ + E = SPH_T32(E + T1); \ + A = SPH_T32(T1 + T2); \ + (r)[0] = SPH_T32((r)[0] + A); \ + (r)[1] = SPH_T32((r)[1] + B); \ + (r)[2] = SPH_T32((r)[2] + C); \ + (r)[3] = SPH_T32((r)[3] + D); \ + (r)[4] = SPH_T32((r)[4] + E); \ + (r)[5] = SPH_T32((r)[5] + F); \ + (r)[6] = SPH_T32((r)[6] + G); \ + (r)[7] = SPH_T32((r)[7] + H); \ + } while (0) + +#endif + +/* + * One round of SHA-224 / SHA-256. The data must be aligned for 32-bit access. + */ +static void +sha2_round(const unsigned char *data, sph_u32 r[8]) +{ +#define SHA2_IN(x) sph_dec32be_aligned(data + (4 * (x))) + SHA2_ROUND_BODY(SHA2_IN, r); +#undef SHA2_IN +} + +/* see sph_sha2.h */ +void +sph_sha224_init(void *cc) +{ + sph_sha224_context *sc; + + sc = cc; + memcpy(sc->val, H224, sizeof H224); +#if SPH_64 + sc->count = 0; +#else + sc->count_high = sc->count_low = 0; +#endif +} + +/* see sph_sha2.h */ +void +sph_sha256_init(void *cc) +{ + sph_sha256_context *sc; + + sc = cc; + memcpy(sc->val, H256, sizeof H256); +#if SPH_64 + sc->count = 0; +#else + sc->count_high = sc->count_low = 0; +#endif +} + +#define RFUN sha2_round +#define HASH sha224 +#define BE32 1 +#include "sph/md_helper.c" + +/* see sph_sha2.h */ +void +sph_sha224_close(void *cc, void *dst) +{ + sha224_close(cc, dst, 7); + sph_sha224_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha224_addbits_and_close(cc, ub, n, dst, 7); + sph_sha224_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha256_close(void *cc, void *dst) +{ + sha224_close(cc, dst, 8); + sph_sha256_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha224_addbits_and_close(cc, ub, n, dst, 8); + sph_sha256_init(cc); +} + +/* see sph_sha2.h */ +void +sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]) +{ +#define SHA2_IN(x) msg[x] + SHA2_ROUND_BODY(SHA2_IN, val); +#undef SHA2_IN +} diff --git a/libqpdf/sha2big.c b/libqpdf/sha2big.c new file mode 100644 index 0000000..e4aadbd --- /dev/null +++ b/libqpdf/sha2big.c @@ -0,0 +1,247 @@ +/* $Id: sha2big.c 216 2010-06-08 09:46:57Z tp $ */ +/* + * SHA-384 / SHA-512 implementation. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#include +#include + +#include "sph/sph_sha2.h" + +#if SPH_64 + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((X) & (Y)) | (((X) | (Y)) & (Z))) + +#define ROTR64 SPH_ROTR64 + +#define BSG5_0(x) (ROTR64(x, 28) ^ ROTR64(x, 34) ^ ROTR64(x, 39)) +#define BSG5_1(x) (ROTR64(x, 14) ^ ROTR64(x, 18) ^ ROTR64(x, 41)) +#define SSG5_0(x) (ROTR64(x, 1) ^ ROTR64(x, 8) ^ SPH_T64((x) >> 7)) +#define SSG5_1(x) (ROTR64(x, 19) ^ ROTR64(x, 61) ^ SPH_T64((x) >> 6)) + +static const sph_u64 K512[80] = { + SPH_C64(0x428A2F98D728AE22), SPH_C64(0x7137449123EF65CD), + SPH_C64(0xB5C0FBCFEC4D3B2F), SPH_C64(0xE9B5DBA58189DBBC), + SPH_C64(0x3956C25BF348B538), SPH_C64(0x59F111F1B605D019), + SPH_C64(0x923F82A4AF194F9B), SPH_C64(0xAB1C5ED5DA6D8118), + SPH_C64(0xD807AA98A3030242), SPH_C64(0x12835B0145706FBE), + SPH_C64(0x243185BE4EE4B28C), SPH_C64(0x550C7DC3D5FFB4E2), + SPH_C64(0x72BE5D74F27B896F), SPH_C64(0x80DEB1FE3B1696B1), + SPH_C64(0x9BDC06A725C71235), SPH_C64(0xC19BF174CF692694), + SPH_C64(0xE49B69C19EF14AD2), SPH_C64(0xEFBE4786384F25E3), + SPH_C64(0x0FC19DC68B8CD5B5), SPH_C64(0x240CA1CC77AC9C65), + SPH_C64(0x2DE92C6F592B0275), SPH_C64(0x4A7484AA6EA6E483), + SPH_C64(0x5CB0A9DCBD41FBD4), SPH_C64(0x76F988DA831153B5), + SPH_C64(0x983E5152EE66DFAB), SPH_C64(0xA831C66D2DB43210), + SPH_C64(0xB00327C898FB213F), SPH_C64(0xBF597FC7BEEF0EE4), + SPH_C64(0xC6E00BF33DA88FC2), SPH_C64(0xD5A79147930AA725), + SPH_C64(0x06CA6351E003826F), SPH_C64(0x142929670A0E6E70), + SPH_C64(0x27B70A8546D22FFC), SPH_C64(0x2E1B21385C26C926), + SPH_C64(0x4D2C6DFC5AC42AED), SPH_C64(0x53380D139D95B3DF), + SPH_C64(0x650A73548BAF63DE), SPH_C64(0x766A0ABB3C77B2A8), + SPH_C64(0x81C2C92E47EDAEE6), SPH_C64(0x92722C851482353B), + SPH_C64(0xA2BFE8A14CF10364), SPH_C64(0xA81A664BBC423001), + SPH_C64(0xC24B8B70D0F89791), SPH_C64(0xC76C51A30654BE30), + SPH_C64(0xD192E819D6EF5218), SPH_C64(0xD69906245565A910), + SPH_C64(0xF40E35855771202A), SPH_C64(0x106AA07032BBD1B8), + SPH_C64(0x19A4C116B8D2D0C8), SPH_C64(0x1E376C085141AB53), + SPH_C64(0x2748774CDF8EEB99), SPH_C64(0x34B0BCB5E19B48A8), + SPH_C64(0x391C0CB3C5C95A63), SPH_C64(0x4ED8AA4AE3418ACB), + SPH_C64(0x5B9CCA4F7763E373), SPH_C64(0x682E6FF3D6B2B8A3), + SPH_C64(0x748F82EE5DEFB2FC), SPH_C64(0x78A5636F43172F60), + SPH_C64(0x84C87814A1F0AB72), SPH_C64(0x8CC702081A6439EC), + SPH_C64(0x90BEFFFA23631E28), SPH_C64(0xA4506CEBDE82BDE9), + SPH_C64(0xBEF9A3F7B2C67915), SPH_C64(0xC67178F2E372532B), + SPH_C64(0xCA273ECEEA26619C), SPH_C64(0xD186B8C721C0C207), + SPH_C64(0xEADA7DD6CDE0EB1E), SPH_C64(0xF57D4F7FEE6ED178), + SPH_C64(0x06F067AA72176FBA), SPH_C64(0x0A637DC5A2C898A6), + SPH_C64(0x113F9804BEF90DAE), SPH_C64(0x1B710B35131C471B), + SPH_C64(0x28DB77F523047D84), SPH_C64(0x32CAAB7B40C72493), + SPH_C64(0x3C9EBE0A15C9BEBC), SPH_C64(0x431D67C49C100D4C), + SPH_C64(0x4CC5D4BECB3E42B6), SPH_C64(0x597F299CFC657E2A), + SPH_C64(0x5FCB6FAB3AD6FAEC), SPH_C64(0x6C44198C4A475817) +}; + +static const sph_u64 H384[8] = { + SPH_C64(0xCBBB9D5DC1059ED8), SPH_C64(0x629A292A367CD507), + SPH_C64(0x9159015A3070DD17), SPH_C64(0x152FECD8F70E5939), + SPH_C64(0x67332667FFC00B31), SPH_C64(0x8EB44A8768581511), + SPH_C64(0xDB0C2E0D64F98FA7), SPH_C64(0x47B5481DBEFA4FA4) +}; + +static const sph_u64 H512[8] = { + SPH_C64(0x6A09E667F3BCC908), SPH_C64(0xBB67AE8584CAA73B), + SPH_C64(0x3C6EF372FE94F82B), SPH_C64(0xA54FF53A5F1D36F1), + SPH_C64(0x510E527FADE682D1), SPH_C64(0x9B05688C2B3E6C1F), + SPH_C64(0x1F83D9ABFB41BD6B), SPH_C64(0x5BE0CD19137E2179) +}; + +/* + * This macro defines the body for a SHA-384 / SHA-512 compression function + * implementation. The "in" parameter should evaluate, when applied to a + * numerical input parameter from 0 to 15, to an expression which yields + * the corresponding input block. The "r" parameter should evaluate to + * an array or pointer expression designating the array of 8 words which + * contains the input and output of the compression function. + * + * SHA-512 is hard for the compiler. If the loop is completely unrolled, + * then the code will be quite huge (possibly more than 100 kB), and the + * performance will be degraded due to cache misses on the code. We + * unroll only eight steps, which avoids all needless copies when + * 64-bit registers are swapped. + */ + +#define SHA3_STEP(A, B, C, D, E, F, G, H, i) do { \ + sph_u64 T1, T2; \ + T1 = SPH_T64(H + BSG5_1(E) + CH(E, F, G) + K512[i] + W[i]); \ + T2 = SPH_T64(BSG5_0(A) + MAJ(A, B, C)); \ + D = SPH_T64(D + T1); \ + H = SPH_T64(T1 + T2); \ + } while (0) + +#define SHA3_ROUND_BODY(in, r) do { \ + int i; \ + sph_u64 A, B, C, D, E, F, G, H; \ + sph_u64 W[80]; \ + \ + for (i = 0; i < 16; i ++) \ + W[i] = in(i); \ + for (i = 16; i < 80; i ++) \ + W[i] = SPH_T64(SSG5_1(W[i - 2]) + W[i - 7] \ + + SSG5_0(W[i - 15]) + W[i - 16]); \ + A = (r)[0]; \ + B = (r)[1]; \ + C = (r)[2]; \ + D = (r)[3]; \ + E = (r)[4]; \ + F = (r)[5]; \ + G = (r)[6]; \ + H = (r)[7]; \ + for (i = 0; i < 80; i += 8) { \ + SHA3_STEP(A, B, C, D, E, F, G, H, i + 0); \ + SHA3_STEP(H, A, B, C, D, E, F, G, i + 1); \ + SHA3_STEP(G, H, A, B, C, D, E, F, i + 2); \ + SHA3_STEP(F, G, H, A, B, C, D, E, i + 3); \ + SHA3_STEP(E, F, G, H, A, B, C, D, i + 4); \ + SHA3_STEP(D, E, F, G, H, A, B, C, i + 5); \ + SHA3_STEP(C, D, E, F, G, H, A, B, i + 6); \ + SHA3_STEP(B, C, D, E, F, G, H, A, i + 7); \ + } \ + (r)[0] = SPH_T64((r)[0] + A); \ + (r)[1] = SPH_T64((r)[1] + B); \ + (r)[2] = SPH_T64((r)[2] + C); \ + (r)[3] = SPH_T64((r)[3] + D); \ + (r)[4] = SPH_T64((r)[4] + E); \ + (r)[5] = SPH_T64((r)[5] + F); \ + (r)[6] = SPH_T64((r)[6] + G); \ + (r)[7] = SPH_T64((r)[7] + H); \ + } while (0) + +/* + * One round of SHA-384 / SHA-512. The data must be aligned for 64-bit access. + */ +static void +sha3_round(const unsigned char *data, sph_u64 r[8]) +{ +#define SHA3_IN(x) sph_dec64be_aligned(data + (8 * (x))) + SHA3_ROUND_BODY(SHA3_IN, r); +#undef SHA3_IN +} + +/* see sph_sha3.h */ +void +sph_sha384_init(void *cc) +{ + sph_sha384_context *sc; + + sc = cc; + memcpy(sc->val, H384, sizeof H384); + sc->count = 0; +} + +/* see sph_sha3.h */ +void +sph_sha512_init(void *cc) +{ + sph_sha512_context *sc; + + sc = cc; + memcpy(sc->val, H512, sizeof H512); + sc->count = 0; +} + +#define RFUN sha3_round +#define HASH sha384 +#define BE64 1 +#include "sph/md_helper.c" + +/* see sph_sha3.h */ +void +sph_sha384_close(void *cc, void *dst) +{ + sha384_close(cc, dst, 6); + sph_sha384_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha384_addbits_and_close(cc, ub, n, dst, 6); + sph_sha384_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha512_close(void *cc, void *dst) +{ + sha384_close(cc, dst, 8); + sph_sha512_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst) +{ + sha384_addbits_and_close(cc, ub, n, dst, 8); + sph_sha512_init(cc); +} + +/* see sph_sha3.h */ +void +sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]) +{ +#define SHA3_IN(x) msg[x] + SHA3_ROUND_BODY(SHA3_IN, val); +#undef SHA3_IN +} + +#endif diff --git a/libqpdf/sph/md_helper.c b/libqpdf/sph/md_helper.c new file mode 100644 index 0000000..5384f03 --- /dev/null +++ b/libqpdf/sph/md_helper.c @@ -0,0 +1,346 @@ +/* $Id: md_helper.c 216 2010-06-08 09:46:57Z tp $ */ +/* + * This file contains some functions which implement the external data + * handling and padding for Merkle-Damgard hash functions which follow + * the conventions set out by MD4 (little-endian) or SHA-1 (big-endian). + * + * API: this file is meant to be included, not compiled as a stand-alone + * file. Some macros must be defined: + * RFUN name for the round function + * HASH "short name" for the hash function + * BE32 defined for big-endian, 32-bit based (e.g. SHA-1) + * LE32 defined for little-endian, 32-bit based (e.g. MD5) + * BE64 defined for big-endian, 64-bit based (e.g. SHA-512) + * LE64 defined for little-endian, 64-bit based (no example yet) + * PW01 if defined, append 0x01 instead of 0x80 (for Tiger) + * BLEN if defined, length of a message block (in bytes) + * PLW1 if defined, length is defined on one 64-bit word only (for Tiger) + * PLW4 if defined, length is defined on four 64-bit words (for WHIRLPOOL) + * SVAL if defined, reference to the context state information + * + * BLEN is used when a message block is not 16 (32-bit or 64-bit) words: + * this is used for instance for Tiger, which works on 64-bit words but + * uses 512-bit message blocks (eight 64-bit words). PLW1 and PLW4 are + * ignored if 32-bit words are used; if 64-bit words are used and PLW1 is + * set, then only one word (64 bits) will be used to encode the input + * message length (in bits), otherwise two words will be used (as in + * SHA-384 and SHA-512). If 64-bit words are used and PLW4 is defined (but + * not PLW1), four 64-bit words will be used to encode the message length + * (in bits). Note that regardless of those settings, only 64-bit message + * lengths are supported (in bits): messages longer than 2 Exabytes will be + * improperly hashed (this is unlikely to happen soon: 2 Exabytes is about + * 2 millions Terabytes, which is huge). + * + * If CLOSE_ONLY is defined, then this file defines only the sph_XXX_close() + * function. This is used for Tiger2, which is identical to Tiger except + * when it comes to the padding (Tiger2 uses the standard 0x80 byte instead + * of the 0x01 from original Tiger). + * + * The RFUN function is invoked with two arguments, the first pointing to + * aligned data (as a "const void *"), the second being state information + * from the context structure. By default, this state information is the + * "val" field from the context, and this field is assumed to be an array + * of words ("sph_u32" or "sph_u64", depending on BE32/LE32/BE64/LE64). + * from the context structure. The "val" field can have any type, except + * for the output encoding which assumes that it is an array of "sph_u32" + * values. By defining NO_OUTPUT, this last step is deactivated; the + * includer code is then responsible for writing out the hash result. When + * NO_OUTPUT is defined, the third parameter to the "close()" function is + * ignored. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @author Thomas Pornin + */ + +#ifdef _MSC_VER +#pragma warning (disable: 4146) +#endif + +#undef SPH_XCAT +#define SPH_XCAT(a, b) SPH_XCAT_(a, b) +#undef SPH_XCAT_ +#define SPH_XCAT_(a, b) a ## b + +#undef SPH_BLEN +#undef SPH_WLEN +#if defined BE64 || defined LE64 +#define SPH_BLEN 128U +#define SPH_WLEN 8U +#else +#define SPH_BLEN 64U +#define SPH_WLEN 4U +#endif + +#ifdef BLEN +#undef SPH_BLEN +#define SPH_BLEN BLEN +#endif + +#undef SPH_MAXPAD +#if defined PLW1 +#define SPH_MAXPAD (SPH_BLEN - SPH_WLEN) +#elif defined PLW4 +#define SPH_MAXPAD (SPH_BLEN - (SPH_WLEN << 2)) +#else +#define SPH_MAXPAD (SPH_BLEN - (SPH_WLEN << 1)) +#endif + +#undef SPH_VAL +#undef SPH_NO_OUTPUT +#ifdef SVAL +#define SPH_VAL SVAL +#define SPH_NO_OUTPUT 1 +#else +#define SPH_VAL sc->val +#endif + +#ifndef CLOSE_ONLY + +#ifdef SPH_UPTR +static void +SPH_XCAT(HASH, _short)(void *cc, const void *data, size_t len) +#else +void +SPH_XCAT(sph_, HASH)(void *cc, const void *data, size_t len) +#endif +{ + SPH_XCAT(sph_, SPH_XCAT(HASH, _context)) *sc; + unsigned current; + + sc = cc; +#if SPH_64 + current = (unsigned)sc->count & (SPH_BLEN - 1U); +#else + current = (unsigned)sc->count_low & (SPH_BLEN - 1U); +#endif + while (len > 0) { + unsigned clen; +#if !SPH_64 + sph_u32 clow, clow2; +#endif + + clen = SPH_BLEN - current; + if (clen > len) + clen = len; + memcpy(sc->buf + current, data, clen); + data = (const unsigned char *)data + clen; + current += clen; + len -= clen; + if (current == SPH_BLEN) { + RFUN(sc->buf, SPH_VAL); + current = 0; + } +#if SPH_64 + sc->count += clen; +#else + clow = sc->count_low; + clow2 = SPH_T32(clow + clen); + sc->count_low = clow2; + if (clow2 < clow) + sc->count_high ++; +#endif + } +} + +#ifdef SPH_UPTR +void +SPH_XCAT(sph_, HASH)(void *cc, const void *data, size_t len) +{ + SPH_XCAT(sph_, SPH_XCAT(HASH, _context)) *sc; + unsigned current; + size_t orig_len; +#if !SPH_64 + sph_u32 clow, clow2; +#endif + + if (len < (2 * SPH_BLEN)) { + SPH_XCAT(HASH, _short)(cc, data, len); + return; + } + sc = cc; +#if SPH_64 + current = (unsigned)sc->count & (SPH_BLEN - 1U); +#else + current = (unsigned)sc->count_low & (SPH_BLEN - 1U); +#endif + if (current > 0) { + unsigned t; + + t = SPH_BLEN - current; + SPH_XCAT(HASH, _short)(cc, data, t); + data = (const unsigned char *)data + t; + len -= t; + } +#if !SPH_UNALIGNED + if (((SPH_UPTR)data & (SPH_WLEN - 1U)) != 0) { + SPH_XCAT(HASH, _short)(cc, data, len); + return; + } +#endif + orig_len = len; + while (len >= SPH_BLEN) { + RFUN(data, SPH_VAL); + len -= SPH_BLEN; + data = (const unsigned char *)data + SPH_BLEN; + } + if (len > 0) + memcpy(sc->buf, data, len); +#if SPH_64 + sc->count += (sph_u64)orig_len; +#else + clow = sc->count_low; + clow2 = SPH_T32(clow + orig_len); + sc->count_low = clow2; + if (clow2 < clow) + sc->count_high ++; + /* + * This code handles the improbable situation where "size_t" is + * greater than 32 bits, and yet we do not have a 64-bit type. + */ + orig_len >>= 12; + orig_len >>= 10; + orig_len >>= 10; + sc->count_high += orig_len; +#endif +} +#endif + +#endif + +/* + * Perform padding and produce result. The context is NOT reinitialized + * by this function. + */ +static void +SPH_XCAT(HASH, _addbits_and_close)(void *cc, + unsigned ub, unsigned n, void *dst, unsigned rnum) +{ + SPH_XCAT(sph_, SPH_XCAT(HASH, _context)) *sc; + unsigned current, u; +#if !SPH_64 + sph_u32 low, high; +#endif + + sc = cc; +#if SPH_64 + current = (unsigned)sc->count & (SPH_BLEN - 1U); +#else + current = (unsigned)sc->count_low & (SPH_BLEN - 1U); +#endif +#ifdef PW01 + sc->buf[current ++] = (0x100 | (ub & 0xFF)) >> (8 - n); +#else + { + unsigned z; + + z = 0x80 >> n; + sc->buf[current ++] = ((ub & -z) | z) & 0xFF; + } +#endif + if (current > SPH_MAXPAD) { + memset(sc->buf + current, 0, SPH_BLEN - current); + RFUN(sc->buf, SPH_VAL); + memset(sc->buf, 0, SPH_MAXPAD); + } else { + memset(sc->buf + current, 0, SPH_MAXPAD - current); + } +#if defined BE64 +#if defined PLW1 + sph_enc64be_aligned(sc->buf + SPH_MAXPAD, + SPH_T64(sc->count << 3) + (sph_u64)n); +#elif defined PLW4 + memset(sc->buf + SPH_MAXPAD, 0, 2 * SPH_WLEN); + sph_enc64be_aligned(sc->buf + SPH_MAXPAD + 2 * SPH_WLEN, + sc->count >> 61); + sph_enc64be_aligned(sc->buf + SPH_MAXPAD + 3 * SPH_WLEN, + SPH_T64(sc->count << 3) + (sph_u64)n); +#else + sph_enc64be_aligned(sc->buf + SPH_MAXPAD, sc->count >> 61); + sph_enc64be_aligned(sc->buf + SPH_MAXPAD + SPH_WLEN, + SPH_T64(sc->count << 3) + (sph_u64)n); +#endif +#elif defined LE64 +#if defined PLW1 + sph_enc64le_aligned(sc->buf + SPH_MAXPAD, + SPH_T64(sc->count << 3) + (sph_u64)n); +#elif defined PLW1 + sph_enc64le_aligned(sc->buf + SPH_MAXPAD, + SPH_T64(sc->count << 3) + (sph_u64)n); + sph_enc64le_aligned(sc->buf + SPH_MAXPAD + SPH_WLEN, sc->count >> 61); + memset(sc->buf + SPH_MAXPAD + 2 * SPH_WLEN, 0, 2 * SPH_WLEN); +#else + sph_enc64le_aligned(sc->buf + SPH_MAXPAD, + SPH_T64(sc->count << 3) + (sph_u64)n); + sph_enc64le_aligned(sc->buf + SPH_MAXPAD + SPH_WLEN, sc->count >> 61); +#endif +#else +#if SPH_64 +#ifdef BE32 + sph_enc64be_aligned(sc->buf + SPH_MAXPAD, + SPH_T64(sc->count << 3) + (sph_u64)n); +#else + sph_enc64le_aligned(sc->buf + SPH_MAXPAD, + SPH_T64(sc->count << 3) + (sph_u64)n); +#endif +#else + low = sc->count_low; + high = SPH_T32((sc->count_high << 3) | (low >> 29)); + low = SPH_T32(low << 3) + (sph_u32)n; +#ifdef BE32 + sph_enc32be(sc->buf + SPH_MAXPAD, high); + sph_enc32be(sc->buf + SPH_MAXPAD + SPH_WLEN, low); +#else + sph_enc32le(sc->buf + SPH_MAXPAD, low); + sph_enc32le(sc->buf + SPH_MAXPAD + SPH_WLEN, high); +#endif +#endif +#endif + RFUN(sc->buf, SPH_VAL); +#ifdef SPH_NO_OUTPUT + (void)dst; + (void)rnum; + (void)u; +#else + for (u = 0; u < rnum; u ++) { +#if defined BE64 + sph_enc64be((unsigned char *)dst + 8 * u, sc->val[u]); +#elif defined LE64 + sph_enc64le((unsigned char *)dst + 8 * u, sc->val[u]); +#elif defined BE32 + sph_enc32be((unsigned char *)dst + 4 * u, sc->val[u]); +#else + sph_enc32le((unsigned char *)dst + 4 * u, sc->val[u]); +#endif + } +#endif +} + +static void +SPH_XCAT(HASH, _close)(void *cc, void *dst, unsigned rnum) +{ + SPH_XCAT(HASH, _addbits_and_close)(cc, 0, 0, dst, rnum); +} diff --git a/libqpdf/sph/sph_sha2.h b/libqpdf/sph/sph_sha2.h new file mode 100644 index 0000000..4bff9cd --- /dev/null +++ b/libqpdf/sph/sph_sha2.h @@ -0,0 +1,378 @@ +/* $Id: sph_sha2.h 216 2010-06-08 09:46:57Z tp $ */ +/** + * SHA-224, SHA-256, SHA-384 and SHA-512 interface. + * + * SHA-256 has been published in FIPS 180-2, now amended with a change + * notice to include SHA-224 as well (which is a simple variation on + * SHA-256). SHA-384 and SHA-512 are also defined in FIPS 180-2. FIPS + * standards can be found at: + * http://csrc.nist.gov/publications/fips/ + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_sha2.h + * @author Thomas Pornin + */ + +#ifndef SPH_SHA2_H__ +#define SPH_SHA2_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "sph_types.h" + +/** + * Output size (in bits) for SHA-224. + */ +#define SPH_SIZE_sha224 224 + +/** + * Output size (in bits) for SHA-256. + */ +#define SPH_SIZE_sha256 256 + +/** + * This structure is a context for SHA-224 computations: it contains the + * intermediate values and some data from the last entered block. Once + * a SHA-224 computation has been performed, the context can be reused for + * another computation. + * + * The contents of this structure are private. A running SHA-224 computation + * can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { +#ifndef DOXYGEN_IGNORE + unsigned char buf[64]; /* first field, for alignment */ + sph_u32 val[8]; +#if SPH_64 + sph_u64 count; +#else + sph_u32 count_high, count_low; +#endif +#endif +} sph_sha224_context; + +/** + * This structure is a context for SHA-256 computations. It is identical + * to the SHA-224 context. However, a context is initialized for SHA-224 + * or SHA-256, but not both (the internal IV is not the + * same). + */ +typedef sph_sha224_context sph_sha256_context; + +/** + * Initialize a SHA-224 context. This process performs no memory allocation. + * + * @param cc the SHA-224 context (pointer to + * a sph_sha224_context) + */ +void sph_sha224_init(void *cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the SHA-224 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha224(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-224 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (28 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-224 context + * @param dst the destination buffer + */ +void sph_sha224_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (28 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-224 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha224_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +/** + * Apply the SHA-224 compression function on the provided data. The + * msg parameter contains the 16 32-bit input blocks, + * as numerical values (hence after the big-endian decoding). The + * val parameter contains the 8 32-bit input blocks for + * the compression function; the output is written in place in this + * array. + * + * @param msg the message block (16 values) + * @param val the function 256-bit input and output + */ +void sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]); + +/** + * Initialize a SHA-256 context. This process performs no memory allocation. + * + * @param cc the SHA-256 context (pointer to + * a sph_sha256_context) + */ +void sph_sha256_init(void *cc); + +#ifdef DOXYGEN_IGNORE +/** + * Process some data bytes, for SHA-256. This function is identical to + * sha_224() + * + * @param cc the SHA-224 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha256(void *cc, const void *data, size_t len); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha256 sph_sha224 +#endif + +/** + * Terminate the current SHA-256 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (32 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-256 context + * @param dst the destination buffer + */ +void sph_sha256_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (32 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-256 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha256_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +#ifdef DOXYGEN_IGNORE +/** + * Apply the SHA-256 compression function on the provided data. This + * function is identical to sha224_comp(). + * + * @param msg the message block (16 values) + * @param val the function 256-bit input and output + */ +void sph_sha256_comp(const sph_u32 msg[16], sph_u32 val[8]); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha256_comp sph_sha224_comp +#endif + +#if SPH_64 + +/** + * Output size (in bits) for SHA-384. + */ +#define SPH_SIZE_sha384 384 + +/** + * Output size (in bits) for SHA-512. + */ +#define SPH_SIZE_sha512 512 + +/** + * This structure is a context for SHA-384 computations: it contains the + * intermediate values and some data from the last entered block. Once + * a SHA-384 computation has been performed, the context can be reused for + * another computation. + * + * The contents of this structure are private. A running SHA-384 computation + * can be cloned by copying the context (e.g. with a simple + * memcpy()). + */ +typedef struct { +#ifndef DOXYGEN_IGNORE + unsigned char buf[128]; /* first field, for alignment */ + sph_u64 val[8]; + sph_u64 count; +#endif +} sph_sha384_context; + +/** + * Initialize a SHA-384 context. This process performs no memory allocation. + * + * @param cc the SHA-384 context (pointer to + * a sph_sha384_context) + */ +void sph_sha384_init(void *cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha384(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-384 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (48 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-384 context + * @param dst the destination buffer + */ +void sph_sha384_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (48 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-384 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha384_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +/** + * Apply the SHA-384 compression function on the provided data. The + * msg parameter contains the 16 64-bit input blocks, + * as numerical values (hence after the big-endian decoding). The + * val parameter contains the 8 64-bit input blocks for + * the compression function; the output is written in place in this + * array. + * + * @param msg the message block (16 values) + * @param val the function 512-bit input and output + */ +void sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]); + +/** + * This structure is a context for SHA-512 computations. It is identical + * to the SHA-384 context. However, a context is initialized for SHA-384 + * or SHA-512, but not both (the internal IV is not the + * same). + */ +typedef sph_sha384_context sph_sha512_context; + +/** + * Initialize a SHA-512 context. This process performs no memory allocation. + * + * @param cc the SHA-512 context (pointer to + * a sph_sha512_context) + */ +void sph_sha512_init(void *cc); + +#ifdef DOXYGEN_IGNORE +/** + * Process some data bytes, for SHA-512. This function is identical to + * sph_sha384(). + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha512(void *cc, const void *data, size_t len); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha512 sph_sha384 +#endif + +/** + * Terminate the current SHA-512 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (64 bytes). The context is automatically + * reinitialized. + * + * @param cc the SHA-512 context + * @param dst the destination buffer + */ +void sph_sha512_close(void *cc, void *dst); + +/** + * Add a few additional bits (0 to 7) to the current computation, then + * terminate it and output the result in the provided buffer, which must + * be wide enough to accomodate the result (64 bytes). If bit number i + * in ub has value 2^i, then the extra bits are those + * numbered 7 downto 8-n (this is the big-endian convention at the byte + * level). The context is automatically reinitialized. + * + * @param cc the SHA-512 context + * @param ub the extra bits + * @param n the number of extra bits (0 to 7) + * @param dst the destination buffer + */ +void sph_sha512_addbits_and_close(void *cc, unsigned ub, unsigned n, void *dst); + +#ifdef DOXYGEN_IGNORE +/** + * Apply the SHA-512 compression function. This function is identical to + * sph_sha384_comp(). + * + * @param msg the message block (16 values) + * @param val the function 512-bit input and output + */ +void sph_sha512_comp(const sph_u64 msg[16], sph_u64 val[8]); +#endif + +#ifndef DOXYGEN_IGNORE +#define sph_sha512_comp sph_sha384_comp +#endif + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libqpdf/sph/sph_types.h b/libqpdf/sph/sph_types.h new file mode 100644 index 0000000..7295b0b --- /dev/null +++ b/libqpdf/sph/sph_types.h @@ -0,0 +1,1976 @@ +/* $Id: sph_types.h 260 2011-07-21 01:02:38Z tp $ */ +/** + * Basic type definitions. + * + * This header file defines the generic integer types that will be used + * for the implementation of hash functions; it also contains helper + * functions which encode and decode multi-byte integer values, using + * either little-endian or big-endian conventions. + * + * This file contains a compile-time test on the size of a byte + * (the unsigned char C type). If bytes are not octets, + * i.e. if they do not have a size of exactly 8 bits, then compilation + * is aborted. Architectures where bytes are not octets are relatively + * rare, even in the embedded devices market. We forbid non-octet bytes + * because there is no clear convention on how octet streams are encoded + * on such systems. + * + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * + * @file sph_types.h + * @author Thomas Pornin + */ + +#ifndef SPH_TYPES_H__ +#define SPH_TYPES_H__ + +#include + +/* + * All our I/O functions are defined over octet streams. We do not know + * how to handle input data if bytes are not octets. + */ +#if CHAR_BIT != 8 +#error This code requires 8-bit bytes +#endif + +/* ============= BEGIN documentation block for Doxygen ============ */ + +#ifdef DOXYGEN_IGNORE + +/** @mainpage sphlib C code documentation + * + * @section overview Overview + * + * sphlib is a library which contains implementations of + * various cryptographic hash functions. These pages have been generated + * with doxygen and + * document the API for the C implementations. + * + * The API is described in appropriate header files, which are available + * in the "Files" section. Each hash function family has its own header, + * whose name begins with "sph_" and contains the family + * name. For instance, the API for the RIPEMD hash functions is available + * in the header file sph_ripemd.h. + * + * @section principles API structure and conventions + * + * @subsection io Input/output conventions + * + * In all generality, hash functions operate over strings of bits. + * Individual bits are rarely encountered in C programming or actual + * communication protocols; most protocols converge on the ubiquitous + * "octet" which is a group of eight bits. Data is thus expressed as a + * stream of octets. The C programming language contains the notion of a + * "byte", which is a data unit managed under the type "unsigned + * char". The C standard prescribes that a byte should hold at + * least eight bits, but possibly more. Most modern architectures, even + * in the embedded world, feature eight-bit bytes, i.e. map bytes to + * octets. + * + * Nevertheless, for some of the implemented hash functions, an extra + * API has been added, which allows the input of arbitrary sequences of + * bits: when the computation is about to be closed, 1 to 7 extra bits + * can be added. The functions for which this API is implemented include + * the SHA-2 functions and all SHA-3 candidates. + * + * sphlib defines hash function which may hash octet streams, + * i.e. streams of bits where the number of bits is a multiple of eight. + * The data input functions in the sphlib API expect data + * as anonymous pointers ("const void *") with a length + * (of type "size_t") which gives the input data chunk length + * in bytes. A byte is assumed to be an octet; the sph_types.h + * header contains a compile-time test which prevents compilation on + * architectures where this property is not met. + * + * The hash function output is also converted into bytes. All currently + * implemented hash functions have an output width which is a multiple of + * eight, and this is likely to remain true for new designs. + * + * Most hash functions internally convert input data into 32-bit of 64-bit + * words, using either little-endian or big-endian conversion. The hash + * output also often consists of such words, which are encoded into output + * bytes with a similar endianness convention. Some hash functions have + * been only loosely specified on that subject; when necessary, + * sphlib has been tested against published "reference" + * implementations in order to use the same conventions. + * + * @subsection shortname Function short name + * + * Each implemented hash function has a "short name" which is used + * internally to derive the identifiers for the functions and context + * structures which the function uses. For instance, MD5 has the short + * name "md5". Short names are listed in the next section, + * for the implemented hash functions. In subsequent sections, the + * short name will be assumed to be "XXX": replace with the + * actual hash function name to get the C identifier. + * + * Note: some functions within the same family share the same core + * elements, such as update function or context structure. Correspondingly, + * some of the defined types or functions may actually be macros which + * transparently evaluate to another type or function name. + * + * @subsection context Context structure + * + * Each implemented hash fonction has its own context structure, available + * under the type name "sph_XXX_context" for the hash function + * with short name "XXX". This structure holds all needed + * state for a running hash computation. + * + * The contents of these structures are meant to be opaque, and private + * to the implementation. However, these contents are specified in the + * header files so that application code which uses sphlib + * may access the size of those structures. + * + * The caller is responsible for allocating the context structure, + * whether by dynamic allocation (malloc() or equivalent), + * static allocation (a global permanent variable), as an automatic + * variable ("on the stack"), or by any other mean which ensures proper + * structure alignment. sphlib code performs no dynamic + * allocation by itself. + * + * The context must be initialized before use, using the + * sph_XXX_init() function. This function sets the context + * state to proper initial values for hashing. + * + * Since all state data is contained within the context structure, + * sphlib is thread-safe and reentrant: several hash + * computations may be performed in parallel, provided that they do not + * operate on the same context. Moreover, a running computation can be + * cloned by copying the context (with a simple memcpy()): + * the context and its clone are then independant and may be updated + * with new data and/or closed without interfering with each other. + * Similarly, a context structure can be moved in memory at will: + * context structures contain no pointer, in particular no pointer to + * themselves. + * + * @subsection dataio Data input + * + * Hashed data is input with the sph_XXX() fonction, which + * takes as parameters a pointer to the context, a pointer to the data + * to hash, and the number of data bytes to hash. The context is updated + * with the new data. + * + * Data can be input in one or several calls, with arbitrary input lengths. + * However, it is best, performance wise, to input data by relatively big + * chunks (say a few kilobytes), because this allows sphlib to + * optimize things and avoid internal copying. + * + * When all data has been input, the context can be closed with + * sph_XXX_close(). The hash output is computed and written + * into the provided buffer. The caller must take care to provide a + * buffer of appropriate length; e.g., when using SHA-1, the output is + * a 20-byte word, therefore the output buffer must be at least 20-byte + * long. + * + * For some hash functions, the sph_XXX_addbits_and_close() + * function can be used instead of sph_XXX_close(). This + * function can take a few extra bits to be added at + * the end of the input message. This allows hashing messages with a + * bit length which is not a multiple of 8. The extra bits are provided + * as an unsigned integer value, and a bit count. The bit count must be + * between 0 and 7, inclusive. The extra bits are provided as bits 7 to + * 0 (bits of numerical value 128, 64, 32... downto 0), in that order. + * For instance, to add three bits of value 1, 1 and 0, the unsigned + * integer will have value 192 (1*128 + 1*64 + 0*32) and the bit count + * will be 3. + * + * The SPH_SIZE_XXX macro is defined for each hash function; + * it evaluates to the function output size, expressed in bits. For instance, + * SPH_SIZE_sha1 evaluates to 160. + * + * When closed, the context is automatically reinitialized and can be + * immediately used for another computation. It is not necessary to call + * sph_XXX_init() after a close. Note that + * sph_XXX_init() can still be called to "reset" a context, + * i.e. forget previously input data, and get back to the initial state. + * + * @subsection alignment Data alignment + * + * "Alignment" is a property of data, which is said to be "properly + * aligned" when its emplacement in memory is such that the data can + * be optimally read by full words. This depends on the type of access; + * basically, some hash functions will read data by 32-bit or 64-bit + * words. sphlib does not mandate such alignment for input + * data, but using aligned data can substantially improve performance. + * + * As a rule, it is best to input data by chunks whose length (in bytes) + * is a multiple of eight, and which begins at "generally aligned" + * addresses, such as the base address returned by a call to + * malloc(). + * + * @section functions Implemented functions + * + * We give here the list of implemented functions. They are grouped by + * family; to each family corresponds a specific header file. Each + * individual function has its associated "short name". Please refer to + * the documentation for that header file to get details on the hash + * function denomination and provenance. + * + * Note: the functions marked with a '(64)' in the list below are + * available only if the C compiler provides an integer type of length + * 64 bits or more. Such a type is mandatory in the latest C standard + * (ISO 9899:1999, aka "C99") and is present in several older compilers + * as well, so chances are that such a type is available. + * + * - HAVAL family: file sph_haval.h + * - HAVAL-128/3 (128-bit, 3 passes): short name: haval128_3 + * - HAVAL-128/4 (128-bit, 4 passes): short name: haval128_4 + * - HAVAL-128/5 (128-bit, 5 passes): short name: haval128_5 + * - HAVAL-160/3 (160-bit, 3 passes): short name: haval160_3 + * - HAVAL-160/4 (160-bit, 4 passes): short name: haval160_4 + * - HAVAL-160/5 (160-bit, 5 passes): short name: haval160_5 + * - HAVAL-192/3 (192-bit, 3 passes): short name: haval192_3 + * - HAVAL-192/4 (192-bit, 4 passes): short name: haval192_4 + * - HAVAL-192/5 (192-bit, 5 passes): short name: haval192_5 + * - HAVAL-224/3 (224-bit, 3 passes): short name: haval224_3 + * - HAVAL-224/4 (224-bit, 4 passes): short name: haval224_4 + * - HAVAL-224/5 (224-bit, 5 passes): short name: haval224_5 + * - HAVAL-256/3 (256-bit, 3 passes): short name: haval256_3 + * - HAVAL-256/4 (256-bit, 4 passes): short name: haval256_4 + * - HAVAL-256/5 (256-bit, 5 passes): short name: haval256_5 + * - MD2: file sph_md2.h, short name: md2 + * - MD4: file sph_md4.h, short name: md4 + * - MD5: file sph_md5.h, short name: md5 + * - PANAMA: file sph_panama.h, short name: panama + * - RadioGatun family: file sph_radiogatun.h + * - RadioGatun[32]: short name: radiogatun32 + * - RadioGatun[64]: short name: radiogatun64 (64) + * - RIPEMD family: file sph_ripemd.h + * - RIPEMD: short name: ripemd + * - RIPEMD-128: short name: ripemd128 + * - RIPEMD-160: short name: ripemd160 + * - SHA-0: file sph_sha0.h, short name: sha0 + * - SHA-1: file sph_sha1.h, short name: sha1 + * - SHA-2 family, 32-bit hashes: file sph_sha2.h + * - SHA-224: short name: sha224 + * - SHA-256: short name: sha256 + * - SHA-384: short name: sha384 (64) + * - SHA-512: short name: sha512 (64) + * - Tiger family: file sph_tiger.h + * - Tiger: short name: tiger (64) + * - Tiger2: short name: tiger2 (64) + * - WHIRLPOOL family: file sph_whirlpool.h + * - WHIRLPOOL-0: short name: whirlpool0 (64) + * - WHIRLPOOL-1: short name: whirlpool1 (64) + * - WHIRLPOOL: short name: whirlpool (64) + * + * The fourteen second-round SHA-3 candidates are also implemented; + * when applicable, the implementations follow the "final" specifications + * as published for the third round of the SHA-3 competition (BLAKE, + * Groestl, JH, Keccak and Skein have been tweaked for third round). + * + * - BLAKE family: file sph_blake.h + * - BLAKE-224: short name: blake224 + * - BLAKE-256: short name: blake256 + * - BLAKE-384: short name: blake384 + * - BLAKE-512: short name: blake512 + * - BMW (Blue Midnight Wish) family: file sph_bmw.h + * - BMW-224: short name: bmw224 + * - BMW-256: short name: bmw256 + * - BMW-384: short name: bmw384 (64) + * - BMW-512: short name: bmw512 (64) + * - CubeHash family: file sph_cubehash.h (specified as + * CubeHash16/32 in the CubeHash specification) + * - CubeHash-224: short name: cubehash224 + * - CubeHash-256: short name: cubehash256 + * - CubeHash-384: short name: cubehash384 + * - CubeHash-512: short name: cubehash512 + * - ECHO family: file sph_echo.h + * - ECHO-224: short name: echo224 + * - ECHO-256: short name: echo256 + * - ECHO-384: short name: echo384 + * - ECHO-512: short name: echo512 + * - Fugue family: file sph_fugue.h + * - Fugue-224: short name: fugue224 + * - Fugue-256: short name: fugue256 + * - Fugue-384: short name: fugue384 + * - Fugue-512: short name: fugue512 + * - Groestl family: file sph_groestl.h + * - Groestl-224: short name: groestl224 + * - Groestl-256: short name: groestl256 + * - Groestl-384: short name: groestl384 + * - Groestl-512: short name: groestl512 + * - Hamsi family: file sph_hamsi.h + * - Hamsi-224: short name: hamsi224 + * - Hamsi-256: short name: hamsi256 + * - Hamsi-384: short name: hamsi384 + * - Hamsi-512: short name: hamsi512 + * - JH family: file sph_jh.h + * - JH-224: short name: jh224 + * - JH-256: short name: jh256 + * - JH-384: short name: jh384 + * - JH-512: short name: jh512 + * - Keccak family: file sph_keccak.h + * - Keccak-224: short name: keccak224 + * - Keccak-256: short name: keccak256 + * - Keccak-384: short name: keccak384 + * - Keccak-512: short name: keccak512 + * - Luffa family: file sph_luffa.h + * - Luffa-224: short name: luffa224 + * - Luffa-256: short name: luffa256 + * - Luffa-384: short name: luffa384 + * - Luffa-512: short name: luffa512 + * - Shabal family: file sph_shabal.h + * - Shabal-192: short name: shabal192 + * - Shabal-224: short name: shabal224 + * - Shabal-256: short name: shabal256 + * - Shabal-384: short name: shabal384 + * - Shabal-512: short name: shabal512 + * - SHAvite-3 family: file sph_shavite.h + * - SHAvite-224 (nominally "SHAvite-3 with 224-bit output"): + * short name: shabal224 + * - SHAvite-256 (nominally "SHAvite-3 with 256-bit output"): + * short name: shabal256 + * - SHAvite-384 (nominally "SHAvite-3 with 384-bit output"): + * short name: shabal384 + * - SHAvite-512 (nominally "SHAvite-3 with 512-bit output"): + * short name: shabal512 + * - SIMD family: file sph_simd.h + * - SIMD-224: short name: simd224 + * - SIMD-256: short name: simd256 + * - SIMD-384: short name: simd384 + * - SIMD-512: short name: simd512 + * - Skein family: file sph_skein.h + * - Skein-224 (nominally specified as Skein-512-224): short name: + * skein224 (64) + * - Skein-256 (nominally specified as Skein-512-256): short name: + * skein256 (64) + * - Skein-384 (nominally specified as Skein-512-384): short name: + * skein384 (64) + * - Skein-512 (nominally specified as Skein-512-512): short name: + * skein512 (64) + * + * For the second-round SHA-3 candidates, the functions are as specified + * for round 2, i.e. with the "tweaks" that some candidates added + * between round 1 and round 2. Also, some of the submitted packages for + * round 2 contained errors, in the specification, reference code, or + * both. sphlib implements the corrected versions. + */ + +/** @hideinitializer + * Unsigned integer type whose length is at least 32 bits; on most + * architectures, it will have a width of exactly 32 bits. Unsigned C + * types implement arithmetics modulo a power of 2; use the + * SPH_T32() macro to ensure that the value is truncated + * to exactly 32 bits. Unless otherwise specified, all macros and + * functions which accept sph_u32 values assume that these + * values fit on 32 bits, i.e. do not exceed 2^32-1, even on architectures + * where sph_u32 is larger than that. + */ +typedef __arch_dependant__ sph_u32; + +/** @hideinitializer + * Signed integer type corresponding to sph_u32; it has + * width 32 bits or more. + */ +typedef __arch_dependant__ sph_s32; + +/** @hideinitializer + * Unsigned integer type whose length is at least 64 bits; on most + * architectures which feature such a type, it will have a width of + * exactly 64 bits. C99-compliant platform will have this type; it + * is also defined when the GNU compiler (gcc) is used, and on + * platforms where unsigned long is large enough. If this + * type is not available, then some hash functions which depends on + * a 64-bit type will not be available (most notably SHA-384, SHA-512, + * Tiger and WHIRLPOOL). + */ +typedef __arch_dependant__ sph_u64; + +/** @hideinitializer + * Signed integer type corresponding to sph_u64; it has + * width 64 bits or more. + */ +typedef __arch_dependant__ sph_s64; + +/** + * This macro expands the token x into a suitable + * constant expression of type sph_u32. Depending on + * how this type is defined, a suffix such as UL may + * be appended to the argument. + * + * @param x the token to expand into a suitable constant expression + */ +#define SPH_C32(x) + +/** + * Truncate a 32-bit value to exactly 32 bits. On most systems, this is + * a no-op, recognized as such by the compiler. + * + * @param x the value to truncate (of type sph_u32) + */ +#define SPH_T32(x) + +/** + * Rotate a 32-bit value by a number of bits to the left. The rotate + * count must reside between 1 and 31. This macro assumes that its + * first argument fits in 32 bits (no extra bit allowed on machines where + * sph_u32 is wider); both arguments may be evaluated + * several times. + * + * @param x the value to rotate (of type sph_u32) + * @param n the rotation count (between 1 and 31, inclusive) + */ +#define SPH_ROTL32(x, n) + +/** + * Rotate a 32-bit value by a number of bits to the left. The rotate + * count must reside between 1 and 31. This macro assumes that its + * first argument fits in 32 bits (no extra bit allowed on machines where + * sph_u32 is wider); both arguments may be evaluated + * several times. + * + * @param x the value to rotate (of type sph_u32) + * @param n the rotation count (between 1 and 31, inclusive) + */ +#define SPH_ROTR32(x, n) + +/** + * This macro is defined on systems for which a 64-bit type has been + * detected, and is used for sph_u64. + */ +#define SPH_64 + +/** + * This macro is defined on systems for the "native" integer size is + * 64 bits (64-bit values fit in one register). + */ +#define SPH_64_TRUE + +/** + * This macro expands the token x into a suitable + * constant expression of type sph_u64. Depending on + * how this type is defined, a suffix such as ULL may + * be appended to the argument. This macro is defined only if a + * 64-bit type was detected and used for sph_u64. + * + * @param x the token to expand into a suitable constant expression + */ +#define SPH_C64(x) + +/** + * Truncate a 64-bit value to exactly 64 bits. On most systems, this is + * a no-op, recognized as such by the compiler. This macro is defined only + * if a 64-bit type was detected and used for sph_u64. + * + * @param x the value to truncate (of type sph_u64) + */ +#define SPH_T64(x) + +/** + * Rotate a 64-bit value by a number of bits to the left. The rotate + * count must reside between 1 and 63. This macro assumes that its + * first argument fits in 64 bits (no extra bit allowed on machines where + * sph_u64 is wider); both arguments may be evaluated + * several times. This macro is defined only if a 64-bit type was detected + * and used for sph_u64. + * + * @param x the value to rotate (of type sph_u64) + * @param n the rotation count (between 1 and 63, inclusive) + */ +#define SPH_ROTL64(x, n) + +/** + * Rotate a 64-bit value by a number of bits to the left. The rotate + * count must reside between 1 and 63. This macro assumes that its + * first argument fits in 64 bits (no extra bit allowed on machines where + * sph_u64 is wider); both arguments may be evaluated + * several times. This macro is defined only if a 64-bit type was detected + * and used for sph_u64. + * + * @param x the value to rotate (of type sph_u64) + * @param n the rotation count (between 1 and 63, inclusive) + */ +#define SPH_ROTR64(x, n) + +/** + * This macro evaluates to inline or an equivalent construction, + * if available on the compilation platform, or to nothing otherwise. This + * is used to declare inline functions, for which the compiler should + * endeavour to include the code directly in the caller. Inline functions + * are typically defined in header files as replacement for macros. + */ +#define SPH_INLINE + +/** + * This macro is defined if the platform has been detected as using + * little-endian convention. This implies that the sph_u32 + * type (and the sph_u64 type also, if it is defined) has + * an exact width (i.e. exactly 32-bit, respectively 64-bit). + */ +#define SPH_LITTLE_ENDIAN + +/** + * This macro is defined if the platform has been detected as using + * big-endian convention. This implies that the sph_u32 + * type (and the sph_u64 type also, if it is defined) has + * an exact width (i.e. exactly 32-bit, respectively 64-bit). + */ +#define SPH_BIG_ENDIAN + +/** + * This macro is defined if 32-bit words (and 64-bit words, if defined) + * can be read from and written to memory efficiently in little-endian + * convention. This is the case for little-endian platforms, and also + * for the big-endian platforms which have special little-endian access + * opcodes (e.g. Ultrasparc). + */ +#define SPH_LITTLE_FAST + +/** + * This macro is defined if 32-bit words (and 64-bit words, if defined) + * can be read from and written to memory efficiently in big-endian + * convention. This is the case for little-endian platforms, and also + * for the little-endian platforms which have special big-endian access + * opcodes. + */ +#define SPH_BIG_FAST + +/** + * On some platforms, this macro is defined to an unsigned integer type + * into which pointer values may be cast. The resulting value can then + * be tested for being a multiple of 2, 4 or 8, indicating an aligned + * pointer for, respectively, 16-bit, 32-bit or 64-bit memory accesses. + */ +#define SPH_UPTR + +/** + * When defined, this macro indicates that unaligned memory accesses + * are possible with only a minor penalty, and thus should be prefered + * over strategies which first copy data to an aligned buffer. + */ +#define SPH_UNALIGNED + +/** + * Byte-swap a 32-bit word (i.e. 0x12345678 becomes + * 0x78563412). This is an inline function which resorts + * to inline assembly on some platforms, for better performance. + * + * @param x the 32-bit value to byte-swap + * @return the byte-swapped value + */ +static inline sph_u32 sph_bswap32(sph_u32 x); + +/** + * Byte-swap a 64-bit word. This is an inline function which resorts + * to inline assembly on some platforms, for better performance. This + * function is defined only if a suitable 64-bit type was found for + * sph_u64 + * + * @param x the 64-bit value to byte-swap + * @return the byte-swapped value + */ +static inline sph_u64 sph_bswap64(sph_u64 x); + +/** + * Decode a 16-bit unsigned value from memory, in little-endian convention + * (least significant byte comes first). + * + * @param src the source address + * @return the decoded value + */ +static inline unsigned sph_dec16le(const void *src); + +/** + * Encode a 16-bit unsigned value into memory, in little-endian convention + * (least significant byte comes first). + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc16le(void *dst, unsigned val); + +/** + * Decode a 16-bit unsigned value from memory, in big-endian convention + * (most significant byte comes first). + * + * @param src the source address + * @return the decoded value + */ +static inline unsigned sph_dec16be(const void *src); + +/** + * Encode a 16-bit unsigned value into memory, in big-endian convention + * (most significant byte comes first). + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc16be(void *dst, unsigned val); + +/** + * Decode a 32-bit unsigned value from memory, in little-endian convention + * (least significant byte comes first). + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u32 sph_dec32le(const void *src); + +/** + * Decode a 32-bit unsigned value from memory, in little-endian convention + * (least significant byte comes first). This function assumes that the + * source address is suitably aligned for a direct access, if the platform + * supports such things; it can thus be marginally faster than the generic + * sph_dec32le() function. + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u32 sph_dec32le_aligned(const void *src); + +/** + * Encode a 32-bit unsigned value into memory, in little-endian convention + * (least significant byte comes first). + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc32le(void *dst, sph_u32 val); + +/** + * Encode a 32-bit unsigned value into memory, in little-endian convention + * (least significant byte comes first). This function assumes that the + * destination address is suitably aligned for a direct access, if the + * platform supports such things; it can thus be marginally faster than + * the generic sph_enc32le() function. + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc32le_aligned(void *dst, sph_u32 val); + +/** + * Decode a 32-bit unsigned value from memory, in big-endian convention + * (most significant byte comes first). + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u32 sph_dec32be(const void *src); + +/** + * Decode a 32-bit unsigned value from memory, in big-endian convention + * (most significant byte comes first). This function assumes that the + * source address is suitably aligned for a direct access, if the platform + * supports such things; it can thus be marginally faster than the generic + * sph_dec32be() function. + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u32 sph_dec32be_aligned(const void *src); + +/** + * Encode a 32-bit unsigned value into memory, in big-endian convention + * (most significant byte comes first). + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc32be(void *dst, sph_u32 val); + +/** + * Encode a 32-bit unsigned value into memory, in big-endian convention + * (most significant byte comes first). This function assumes that the + * destination address is suitably aligned for a direct access, if the + * platform supports such things; it can thus be marginally faster than + * the generic sph_enc32be() function. + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc32be_aligned(void *dst, sph_u32 val); + +/** + * Decode a 64-bit unsigned value from memory, in little-endian convention + * (least significant byte comes first). This function is defined only + * if a suitable 64-bit type was detected and used for sph_u64. + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u64 sph_dec64le(const void *src); + +/** + * Decode a 64-bit unsigned value from memory, in little-endian convention + * (least significant byte comes first). This function assumes that the + * source address is suitably aligned for a direct access, if the platform + * supports such things; it can thus be marginally faster than the generic + * sph_dec64le() function. This function is defined only + * if a suitable 64-bit type was detected and used for sph_u64. + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u64 sph_dec64le_aligned(const void *src); + +/** + * Encode a 64-bit unsigned value into memory, in little-endian convention + * (least significant byte comes first). This function is defined only + * if a suitable 64-bit type was detected and used for sph_u64. + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc64le(void *dst, sph_u64 val); + +/** + * Encode a 64-bit unsigned value into memory, in little-endian convention + * (least significant byte comes first). This function assumes that the + * destination address is suitably aligned for a direct access, if the + * platform supports such things; it can thus be marginally faster than + * the generic sph_enc64le() function. This function is defined + * only if a suitable 64-bit type was detected and used for + * sph_u64. + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc64le_aligned(void *dst, sph_u64 val); + +/** + * Decode a 64-bit unsigned value from memory, in big-endian convention + * (most significant byte comes first). This function is defined only + * if a suitable 64-bit type was detected and used for sph_u64. + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u64 sph_dec64be(const void *src); + +/** + * Decode a 64-bit unsigned value from memory, in big-endian convention + * (most significant byte comes first). This function assumes that the + * source address is suitably aligned for a direct access, if the platform + * supports such things; it can thus be marginally faster than the generic + * sph_dec64be() function. This function is defined only + * if a suitable 64-bit type was detected and used for sph_u64. + * + * @param src the source address + * @return the decoded value + */ +static inline sph_u64 sph_dec64be_aligned(const void *src); + +/** + * Encode a 64-bit unsigned value into memory, in big-endian convention + * (most significant byte comes first). This function is defined only + * if a suitable 64-bit type was detected and used for sph_u64. + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc64be(void *dst, sph_u64 val); + +/** + * Encode a 64-bit unsigned value into memory, in big-endian convention + * (most significant byte comes first). This function assumes that the + * destination address is suitably aligned for a direct access, if the + * platform supports such things; it can thus be marginally faster than + * the generic sph_enc64be() function. This function is defined + * only if a suitable 64-bit type was detected and used for + * sph_u64. + * + * @param dst the destination buffer + * @param val the value to encode + */ +static inline void sph_enc64be_aligned(void *dst, sph_u64 val); + +#endif + +/* ============== END documentation block for Doxygen ============= */ + +#ifndef DOXYGEN_IGNORE + +/* + * We want to define the types "sph_u32" and "sph_u64" which hold + * unsigned values of at least, respectively, 32 and 64 bits. These + * tests should select appropriate types for most platforms. The + * macro "SPH_64" is defined if the 64-bit is supported. + */ + +#undef SPH_64 +#undef SPH_64_TRUE + +#if defined __STDC__ && __STDC_VERSION__ >= 199901L + +/* + * On C99 implementations, we can use to get an exact 64-bit + * type, if any, or otherwise use a wider type (which must exist, for + * C99 conformance). + */ + +#include + +#ifdef UINT32_MAX +typedef uint32_t sph_u32; +typedef int32_t sph_s32; +#else +typedef uint_fast32_t sph_u32; +typedef int_fast32_t sph_s32; +#endif +#if !SPH_NO_64 +#ifdef UINT64_MAX +typedef uint64_t sph_u64; +typedef int64_t sph_s64; +#else +typedef uint_fast64_t sph_u64; +typedef int_fast64_t sph_s64; +#endif +#endif + +#define SPH_C32(x) ((sph_u32)(x)) +#if !SPH_NO_64 +#define SPH_C64(x) ((sph_u64)(x)) +#define SPH_64 1 +#endif + +#else + +/* + * On non-C99 systems, we use "unsigned int" if it is wide enough, + * "unsigned long" otherwise. This supports all "reasonable" architectures. + * We have to be cautious: pre-C99 preprocessors handle constants + * differently in '#if' expressions. Hence the shifts to test UINT_MAX. + */ + +#if ((UINT_MAX >> 11) >> 11) >= 0x3FF + +typedef unsigned int sph_u32; +typedef int sph_s32; + +#define SPH_C32(x) ((sph_u32)(x ## U)) + +#else + +typedef unsigned long sph_u32; +typedef long sph_s32; + +#define SPH_C32(x) ((sph_u32)(x ## UL)) + +#endif + +#if !SPH_NO_64 + +/* + * We want a 64-bit type. We use "unsigned long" if it is wide enough (as + * is common on 64-bit architectures such as AMD64, Alpha or Sparcv9), + * "unsigned long long" otherwise, if available. We use ULLONG_MAX to + * test whether "unsigned long long" is available; we also know that + * gcc features this type, even if the libc header do not know it. + */ + +#if ((ULONG_MAX >> 31) >> 31) >= 3 + +typedef unsigned long sph_u64; +typedef long sph_s64; + +#define SPH_C64(x) ((sph_u64)(x ## UL)) + +#define SPH_64 1 + +#elif ((ULLONG_MAX >> 31) >> 31) >= 3 || defined __GNUC__ + +typedef unsigned long long sph_u64; +typedef long long sph_s64; + +#define SPH_C64(x) ((sph_u64)(x ## ULL)) + +#define SPH_64 1 + +#else + +/* + * No 64-bit type... + */ + +#endif + +#endif + +#endif + +/* + * If the "unsigned long" type has length 64 bits or more, then this is + * a "true" 64-bit architectures. This is also true with Visual C on + * amd64, even though the "long" type is limited to 32 bits. + */ +#if SPH_64 && (((ULONG_MAX >> 31) >> 31) >= 3 || defined _M_X64) +#define SPH_64_TRUE 1 +#endif + +/* + * Implementation note: some processors have specific opcodes to perform + * a rotation. Recent versions of gcc recognize the expression above and + * use the relevant opcodes, when appropriate. + */ + +#define SPH_T32(x) ((x) & SPH_C32(0xFFFFFFFF)) +#define SPH_ROTL32(x, n) SPH_T32(((x) << (n)) | ((x) >> (32 - (n)))) +#define SPH_ROTR32(x, n) SPH_ROTL32(x, (32 - (n))) + +#if SPH_64 + +#define SPH_T64(x) ((x) & SPH_C64(0xFFFFFFFFFFFFFFFF)) +#define SPH_ROTL64(x, n) SPH_T64(((x) << (n)) | ((x) >> (64 - (n)))) +#define SPH_ROTR64(x, n) SPH_ROTL64(x, (64 - (n))) + +#endif + +#ifndef DOXYGEN_IGNORE +/* + * Define SPH_INLINE to be an "inline" qualifier, if available. We define + * some small macro-like functions which benefit greatly from being inlined. + */ +#if (defined __STDC__ && __STDC_VERSION__ >= 199901L) || defined __GNUC__ +#define SPH_INLINE inline +#elif defined _MSC_VER +#define SPH_INLINE __inline +#else +#define SPH_INLINE +#endif +#endif + +/* + * We define some macros which qualify the architecture. These macros + * may be explicit set externally (e.g. as compiler parameters). The + * code below sets those macros if they are not already defined. + * + * Most macros are boolean, thus evaluate to either zero or non-zero. + * The SPH_UPTR macro is special, in that it evaluates to a C type, + * or is not defined. + * + * SPH_UPTR if defined: unsigned type to cast pointers into + * + * SPH_UNALIGNED non-zero if unaligned accesses are efficient + * SPH_LITTLE_ENDIAN non-zero if architecture is known to be little-endian + * SPH_BIG_ENDIAN non-zero if architecture is known to be big-endian + * SPH_LITTLE_FAST non-zero if little-endian decoding is fast + * SPH_BIG_FAST non-zero if big-endian decoding is fast + * + * If SPH_UPTR is defined, then encoding and decoding of 32-bit and 64-bit + * values will try to be "smart". Either SPH_LITTLE_ENDIAN or SPH_BIG_ENDIAN + * _must_ be non-zero in those situations. The 32-bit and 64-bit types + * _must_ also have an exact width. + * + * SPH_SPARCV9_GCC_32 UltraSPARC-compatible with gcc, 32-bit mode + * SPH_SPARCV9_GCC_64 UltraSPARC-compatible with gcc, 64-bit mode + * SPH_SPARCV9_GCC UltraSPARC-compatible with gcc + * SPH_I386_GCC x86-compatible (32-bit) with gcc + * SPH_I386_MSVC x86-compatible (32-bit) with Microsoft Visual C + * SPH_AMD64_GCC x86-compatible (64-bit) with gcc + * SPH_AMD64_MSVC x86-compatible (64-bit) with Microsoft Visual C + * SPH_PPC32_GCC PowerPC, 32-bit, with gcc + * SPH_PPC64_GCC PowerPC, 64-bit, with gcc + * + * TODO: enhance automatic detection, for more architectures and compilers. + * Endianness is the most important. SPH_UNALIGNED and SPH_UPTR help with + * some very fast functions (e.g. MD4) when using unaligned input data. + * The CPU-specific-with-GCC macros are useful only for inline assembly, + * normally restrained to this header file. + */ + +/* + * 32-bit x86, aka "i386 compatible". + */ +#if defined __i386__ || defined _M_IX86 + +#define SPH_DETECT_UNALIGNED 1 +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_UPTR sph_u32 +#ifdef __GNUC__ +#define SPH_DETECT_I386_GCC 1 +#endif +#ifdef _MSC_VER +#define SPH_DETECT_I386_MSVC 1 +#endif + +/* + * 64-bit x86, hereafter known as "amd64". + */ +#elif defined __x86_64 || defined _M_X64 + +#define SPH_DETECT_UNALIGNED 1 +#define SPH_DETECT_LITTLE_ENDIAN 1 +#define SPH_DETECT_UPTR sph_u64 +#ifdef __GNUC__ +#define SPH_DETECT_AMD64_GCC 1 +#endif +#ifdef _MSC_VER +#define SPH_DETECT_AMD64_MSVC 1 +#endif + +/* + * 64-bit Sparc architecture (implies v9). + */ +#elif ((defined __sparc__ || defined __sparc) && defined __arch64__) \ + || defined __sparcv9 + +#define SPH_DETECT_BIG_ENDIAN 1 +#define SPH_DETECT_UPTR sph_u64 +#ifdef __GNUC__ +#define SPH_DETECT_SPARCV9_GCC_64 1 +#define SPH_DETECT_LITTLE_FAST 1 +#endif + +/* + * 32-bit Sparc. + */ +#elif (defined __sparc__ || defined __sparc) \ + && !(defined __sparcv9 || defined __arch64__) + +#define SPH_DETECT_BIG_ENDIAN 1 +#define SPH_DETECT_UPTR sph_u32 +#if defined __GNUC__ && defined __sparc_v9__ +#define SPH_DETECT_SPARCV9_GCC_32 1 +#define SPH_DETECT_LITTLE_FAST 1 +#endif + +/* + * ARM, little-endian. + */ +#elif defined __arm__ && __ARMEL__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 + +/* + * MIPS, little-endian. + */ +#elif MIPSEL || _MIPSEL || __MIPSEL || __MIPSEL__ + +#define SPH_DETECT_LITTLE_ENDIAN 1 + +/* + * MIPS, big-endian. + */ +#elif MIPSEB || _MIPSEB || __MIPSEB || __MIPSEB__ + +#define SPH_DETECT_BIG_ENDIAN 1 + +/* + * PowerPC. + */ +#elif defined __powerpc__ || defined __POWERPC__ || defined __ppc__ \ + || defined _ARCH_PPC + +/* + * Note: we do not declare cross-endian access to be "fast": even if + * using inline assembly, implementation should still assume that + * keeping the decoded word in a temporary is faster than decoding + * it again. + */ +#if defined __GNUC__ +#if SPH_64_TRUE +#define SPH_DETECT_PPC64_GCC 1 +#else +#define SPH_DETECT_PPC32_GCC 1 +#endif +#endif + +#if defined __BIG_ENDIAN__ || defined _BIG_ENDIAN +#define SPH_DETECT_BIG_ENDIAN 1 +#elif defined __LITTLE_ENDIAN__ || defined _LITTLE_ENDIAN +#define SPH_DETECT_LITTLE_ENDIAN 1 +#endif + +/* + * Itanium, 64-bit. + */ +#elif defined __ia64 || defined __ia64__ \ + || defined __itanium__ || defined _M_IA64 + +#if defined __BIG_ENDIAN__ || defined _BIG_ENDIAN +#define SPH_DETECT_BIG_ENDIAN 1 +#else +#define SPH_DETECT_LITTLE_ENDIAN 1 +#endif +#if defined __LP64__ || defined _LP64 +#define SPH_DETECT_UPTR sph_u64 +#else +#define SPH_DETECT_UPTR sph_u32 +#endif + +#endif + +#if defined SPH_DETECT_SPARCV9_GCC_32 || defined SPH_DETECT_SPARCV9_GCC_64 +#define SPH_DETECT_SPARCV9_GCC 1 +#endif + +#if defined SPH_DETECT_UNALIGNED && !defined SPH_UNALIGNED +#define SPH_UNALIGNED SPH_DETECT_UNALIGNED +#endif +#if defined SPH_DETECT_UPTR && !defined SPH_UPTR +#define SPH_UPTR SPH_DETECT_UPTR +#endif +#if defined SPH_DETECT_LITTLE_ENDIAN && !defined SPH_LITTLE_ENDIAN +#define SPH_LITTLE_ENDIAN SPH_DETECT_LITTLE_ENDIAN +#endif +#if defined SPH_DETECT_BIG_ENDIAN && !defined SPH_BIG_ENDIAN +#define SPH_BIG_ENDIAN SPH_DETECT_BIG_ENDIAN +#endif +#if defined SPH_DETECT_LITTLE_FAST && !defined SPH_LITTLE_FAST +#define SPH_LITTLE_FAST SPH_DETECT_LITTLE_FAST +#endif +#if defined SPH_DETECT_BIG_FAST && !defined SPH_BIG_FAST +#define SPH_BIG_FAST SPH_DETECT_BIG_FAST +#endif +#if defined SPH_DETECT_SPARCV9_GCC_32 && !defined SPH_SPARCV9_GCC_32 +#define SPH_SPARCV9_GCC_32 SPH_DETECT_SPARCV9_GCC_32 +#endif +#if defined SPH_DETECT_SPARCV9_GCC_64 && !defined SPH_SPARCV9_GCC_64 +#define SPH_SPARCV9_GCC_64 SPH_DETECT_SPARCV9_GCC_64 +#endif +#if defined SPH_DETECT_SPARCV9_GCC && !defined SPH_SPARCV9_GCC +#define SPH_SPARCV9_GCC SPH_DETECT_SPARCV9_GCC +#endif +#if defined SPH_DETECT_I386_GCC && !defined SPH_I386_GCC +#define SPH_I386_GCC SPH_DETECT_I386_GCC +#endif +#if defined SPH_DETECT_I386_MSVC && !defined SPH_I386_MSVC +#define SPH_I386_MSVC SPH_DETECT_I386_MSVC +#endif +#if defined SPH_DETECT_AMD64_GCC && !defined SPH_AMD64_GCC +#define SPH_AMD64_GCC SPH_DETECT_AMD64_GCC +#endif +#if defined SPH_DETECT_AMD64_MSVC && !defined SPH_AMD64_MSVC +#define SPH_AMD64_MSVC SPH_DETECT_AMD64_MSVC +#endif +#if defined SPH_DETECT_PPC32_GCC && !defined SPH_PPC32_GCC +#define SPH_PPC32_GCC SPH_DETECT_PPC32_GCC +#endif +#if defined SPH_DETECT_PPC64_GCC && !defined SPH_PPC64_GCC +#define SPH_PPC64_GCC SPH_DETECT_PPC64_GCC +#endif + +#if SPH_LITTLE_ENDIAN && !defined SPH_LITTLE_FAST +#define SPH_LITTLE_FAST 1 +#endif +#if SPH_BIG_ENDIAN && !defined SPH_BIG_FAST +#define SPH_BIG_FAST 1 +#endif + +#if defined SPH_UPTR && !(SPH_LITTLE_ENDIAN || SPH_BIG_ENDIAN) +#error SPH_UPTR defined, but endianness is not known. +#endif + +#if SPH_I386_GCC && !SPH_NO_ASM + +/* + * On x86 32-bit, with gcc, we use the bswapl opcode to byte-swap 32-bit + * values. + */ + +static SPH_INLINE sph_u32 +sph_bswap32(sph_u32 x) +{ + __asm__ __volatile__ ("bswapl %0" : "=r" (x) : "0" (x)); + return x; +} + +#if SPH_64 + +static SPH_INLINE sph_u64 +sph_bswap64(sph_u64 x) +{ + return ((sph_u64)sph_bswap32((sph_u32)x) << 32) + | (sph_u64)sph_bswap32((sph_u32)(x >> 32)); +} + +#endif + +#elif SPH_AMD64_GCC && !SPH_NO_ASM + +/* + * On x86 64-bit, with gcc, we use the bswapl opcode to byte-swap 32-bit + * and 64-bit values. + */ + +static SPH_INLINE sph_u32 +sph_bswap32(sph_u32 x) +{ + __asm__ __volatile__ ("bswapl %0" : "=r" (x) : "0" (x)); + return x; +} + +#if SPH_64 + +static SPH_INLINE sph_u64 +sph_bswap64(sph_u64 x) +{ + __asm__ __volatile__ ("bswapq %0" : "=r" (x) : "0" (x)); + return x; +} + +#endif + +/* + * Disabled code. Apparently, Microsoft Visual C 2005 is smart enough + * to generate proper opcodes for endianness swapping with the pure C + * implementation below. + * + +#elif SPH_I386_MSVC && !SPH_NO_ASM + +static __inline sph_u32 __declspec(naked) __fastcall +sph_bswap32(sph_u32 x) +{ + __asm { + bswap ecx + mov eax,ecx + ret + } +} + +#if SPH_64 + +static SPH_INLINE sph_u64 +sph_bswap64(sph_u64 x) +{ + return ((sph_u64)sph_bswap32((sph_u32)x) << 32) + | (sph_u64)sph_bswap32((sph_u32)(x >> 32)); +} + +#endif + + * + * [end of disabled code] + */ + +#else + +static SPH_INLINE sph_u32 +sph_bswap32(sph_u32 x) +{ + x = SPH_T32((x << 16) | (x >> 16)); + x = ((x & SPH_C32(0xFF00FF00)) >> 8) + | ((x & SPH_C32(0x00FF00FF)) << 8); + return x; +} + +#if SPH_64 + +/** + * Byte-swap a 64-bit value. + * + * @param x the input value + * @return the byte-swapped value + */ +static SPH_INLINE sph_u64 +sph_bswap64(sph_u64 x) +{ + x = SPH_T64((x << 32) | (x >> 32)); + x = ((x & SPH_C64(0xFFFF0000FFFF0000)) >> 16) + | ((x & SPH_C64(0x0000FFFF0000FFFF)) << 16); + x = ((x & SPH_C64(0xFF00FF00FF00FF00)) >> 8) + | ((x & SPH_C64(0x00FF00FF00FF00FF)) << 8); + return x; +} + +#endif + +#endif + +#if SPH_SPARCV9_GCC && !SPH_NO_ASM + +/* + * On UltraSPARC systems, native ordering is big-endian, but it is + * possible to perform little-endian read accesses by specifying the + * address space 0x88 (ASI_PRIMARY_LITTLE). Basically, either we use + * the opcode "lda [%reg]0x88,%dst", where %reg is the register which + * contains the source address and %dst is the destination register, + * or we use "lda [%reg+imm]%asi,%dst", which uses the %asi register + * to get the address space name. The latter format is better since it + * combines an addition and the actual access in a single opcode; but + * it requires the setting (and subsequent resetting) of %asi, which is + * slow. Some operations (i.e. MD5 compression function) combine many + * successive little-endian read accesses, which may share the same + * %asi setting. The macros below contain the appropriate inline + * assembly. + */ + +#define SPH_SPARCV9_SET_ASI \ + sph_u32 sph_sparcv9_asi; \ + __asm__ __volatile__ ( \ + "rd %%asi,%0\n\twr %%g0,0x88,%%asi" : "=r" (sph_sparcv9_asi)); + +#define SPH_SPARCV9_RESET_ASI \ + __asm__ __volatile__ ("wr %%g0,%0,%%asi" : : "r" (sph_sparcv9_asi)); + +#define SPH_SPARCV9_DEC32LE(base, idx) ({ \ + sph_u32 sph_sparcv9_tmp; \ + __asm__ __volatile__ ("lda [%1+" #idx "*4]%%asi,%0" \ + : "=r" (sph_sparcv9_tmp) : "r" (base)); \ + sph_sparcv9_tmp; \ + }) + +#endif + +static SPH_INLINE void +sph_enc16be(void *dst, unsigned val) +{ + ((unsigned char *)dst)[0] = (val >> 8); + ((unsigned char *)dst)[1] = val; +} + +static SPH_INLINE unsigned +sph_dec16be(const void *src) +{ + return ((unsigned)(((const unsigned char *)src)[0]) << 8) + | (unsigned)(((const unsigned char *)src)[1]); +} + +static SPH_INLINE void +sph_enc16le(void *dst, unsigned val) +{ + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = val >> 8; +} + +static SPH_INLINE unsigned +sph_dec16le(const void *src) +{ + return (unsigned)(((const unsigned char *)src)[0]) + | ((unsigned)(((const unsigned char *)src)[1]) << 8); +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static SPH_INLINE void +sph_enc32be(void *dst, sph_u32 val) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_LITTLE_ENDIAN + val = sph_bswap32(val); +#endif + *(sph_u32 *)dst = val; +#else + if (((SPH_UPTR)dst & 3) == 0) { +#if SPH_LITTLE_ENDIAN + val = sph_bswap32(val); +#endif + *(sph_u32 *)dst = val; + } else { + ((unsigned char *)dst)[0] = (val >> 24); + ((unsigned char *)dst)[1] = (val >> 16); + ((unsigned char *)dst)[2] = (val >> 8); + ((unsigned char *)dst)[3] = val; + } +#endif +#else + ((unsigned char *)dst)[0] = (val >> 24); + ((unsigned char *)dst)[1] = (val >> 16); + ((unsigned char *)dst)[2] = (val >> 8); + ((unsigned char *)dst)[3] = val; +#endif +} + +/** + * Encode a 32-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static SPH_INLINE void +sph_enc32be_aligned(void *dst, sph_u32 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u32 *)dst = sph_bswap32(val); +#elif SPH_BIG_ENDIAN + *(sph_u32 *)dst = val; +#else + ((unsigned char *)dst)[0] = (val >> 24); + ((unsigned char *)dst)[1] = (val >> 16); + ((unsigned char *)dst)[2] = (val >> 8); + ((unsigned char *)dst)[3] = val; +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static SPH_INLINE sph_u32 +sph_dec32be(const void *src) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_LITTLE_ENDIAN + return sph_bswap32(*(const sph_u32 *)src); +#else + return *(const sph_u32 *)src; +#endif +#else + if (((SPH_UPTR)src & 3) == 0) { +#if SPH_LITTLE_ENDIAN + return sph_bswap32(*(const sph_u32 *)src); +#else + return *(const sph_u32 *)src; +#endif + } else { + return ((sph_u32)(((const unsigned char *)src)[0]) << 24) + | ((sph_u32)(((const unsigned char *)src)[1]) << 16) + | ((sph_u32)(((const unsigned char *)src)[2]) << 8) + | (sph_u32)(((const unsigned char *)src)[3]); + } +#endif +#else + return ((sph_u32)(((const unsigned char *)src)[0]) << 24) + | ((sph_u32)(((const unsigned char *)src)[1]) << 16) + | ((sph_u32)(((const unsigned char *)src)[2]) << 8) + | (sph_u32)(((const unsigned char *)src)[3]); +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static SPH_INLINE sph_u32 +sph_dec32be_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return sph_bswap32(*(const sph_u32 *)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u32 *)src; +#else + return ((sph_u32)(((const unsigned char *)src)[0]) << 24) + | ((sph_u32)(((const unsigned char *)src)[1]) << 16) + | ((sph_u32)(((const unsigned char *)src)[2]) << 8) + | (sph_u32)(((const unsigned char *)src)[3]); +#endif +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 32-bit value to encode + */ +static SPH_INLINE void +sph_enc32le(void *dst, sph_u32 val) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_BIG_ENDIAN + val = sph_bswap32(val); +#endif + *(sph_u32 *)dst = val; +#else + if (((SPH_UPTR)dst & 3) == 0) { +#if SPH_BIG_ENDIAN + val = sph_bswap32(val); +#endif + *(sph_u32 *)dst = val; + } else { + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); + } +#endif +#else + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); +#endif +} + +/** + * Encode a 32-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (32-bit aligned) + * @param val the value to encode + */ +static SPH_INLINE void +sph_enc32le_aligned(void *dst, sph_u32 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u32 *)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u32 *)dst = sph_bswap32(val); +#else + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static SPH_INLINE sph_u32 +sph_dec32le(const void *src) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_BIG_ENDIAN + return sph_bswap32(*(const sph_u32 *)src); +#else + return *(const sph_u32 *)src; +#endif +#else + if (((SPH_UPTR)src & 3) == 0) { +#if SPH_BIG_ENDIAN +#if SPH_SPARCV9_GCC && !SPH_NO_ASM + sph_u32 tmp; + + /* + * "__volatile__" is needed here because without it, + * gcc-3.4.3 miscompiles the code and performs the + * access before the test on the address, thus triggering + * a bus error... + */ + __asm__ __volatile__ ( + "lda [%1]0x88,%0" : "=r" (tmp) : "r" (src)); + return tmp; +/* + * On PowerPC, this turns out not to be worth the effort: the inline + * assembly makes GCC optimizer uncomfortable, which tends to nullify + * the decoding gains. + * + * For most hash functions, using this inline assembly trick changes + * hashing speed by less than 5% and often _reduces_ it. The biggest + * gains are for MD4 (+11%) and CubeHash (+30%). For all others, it is + * less then 10%. The speed gain on CubeHash is probably due to the + * chronic shortage of registers that CubeHash endures; for the other + * functions, the generic code appears to be efficient enough already. + * +#elif (SPH_PPC32_GCC || SPH_PPC64_GCC) && !SPH_NO_ASM + sph_u32 tmp; + + __asm__ __volatile__ ( + "lwbrx %0,0,%1" : "=r" (tmp) : "r" (src)); + return tmp; + */ +#else + return sph_bswap32(*(const sph_u32 *)src); +#endif +#else + return *(const sph_u32 *)src; +#endif + } else { + return (sph_u32)(((const unsigned char *)src)[0]) + | ((sph_u32)(((const unsigned char *)src)[1]) << 8) + | ((sph_u32)(((const unsigned char *)src)[2]) << 16) + | ((sph_u32)(((const unsigned char *)src)[3]) << 24); + } +#endif +#else + return (sph_u32)(((const unsigned char *)src)[0]) + | ((sph_u32)(((const unsigned char *)src)[1]) << 8) + | ((sph_u32)(((const unsigned char *)src)[2]) << 16) + | ((sph_u32)(((const unsigned char *)src)[3]) << 24); +#endif +} + +/** + * Decode a 32-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (32-bit aligned) + * @return the decoded value + */ +static SPH_INLINE sph_u32 +sph_dec32le_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return *(const sph_u32 *)src; +#elif SPH_BIG_ENDIAN +#if SPH_SPARCV9_GCC && !SPH_NO_ASM + sph_u32 tmp; + + __asm__ __volatile__ ("lda [%1]0x88,%0" : "=r" (tmp) : "r" (src)); + return tmp; +/* + * Not worth it generally. + * +#elif (SPH_PPC32_GCC || SPH_PPC64_GCC) && !SPH_NO_ASM + sph_u32 tmp; + + __asm__ __volatile__ ("lwbrx %0,0,%1" : "=r" (tmp) : "r" (src)); + return tmp; + */ +#else + return sph_bswap32(*(const sph_u32 *)src); +#endif +#else + return (sph_u32)(((const unsigned char *)src)[0]) + | ((sph_u32)(((const unsigned char *)src)[1]) << 8) + | ((sph_u32)(((const unsigned char *)src)[2]) << 16) + | ((sph_u32)(((const unsigned char *)src)[3]) << 24); +#endif +} + +#if SPH_64 + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static SPH_INLINE void +sph_enc64be(void *dst, sph_u64 val) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_LITTLE_ENDIAN + val = sph_bswap64(val); +#endif + *(sph_u64 *)dst = val; +#else + if (((SPH_UPTR)dst & 7) == 0) { +#if SPH_LITTLE_ENDIAN + val = sph_bswap64(val); +#endif + *(sph_u64 *)dst = val; + } else { + ((unsigned char *)dst)[0] = (val >> 56); + ((unsigned char *)dst)[1] = (val >> 48); + ((unsigned char *)dst)[2] = (val >> 40); + ((unsigned char *)dst)[3] = (val >> 32); + ((unsigned char *)dst)[4] = (val >> 24); + ((unsigned char *)dst)[5] = (val >> 16); + ((unsigned char *)dst)[6] = (val >> 8); + ((unsigned char *)dst)[7] = val; + } +#endif +#else + ((unsigned char *)dst)[0] = (val >> 56); + ((unsigned char *)dst)[1] = (val >> 48); + ((unsigned char *)dst)[2] = (val >> 40); + ((unsigned char *)dst)[3] = (val >> 32); + ((unsigned char *)dst)[4] = (val >> 24); + ((unsigned char *)dst)[5] = (val >> 16); + ((unsigned char *)dst)[6] = (val >> 8); + ((unsigned char *)dst)[7] = val; +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (big endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static SPH_INLINE void +sph_enc64be_aligned(void *dst, sph_u64 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u64 *)dst = sph_bswap64(val); +#elif SPH_BIG_ENDIAN + *(sph_u64 *)dst = val; +#else + ((unsigned char *)dst)[0] = (val >> 56); + ((unsigned char *)dst)[1] = (val >> 48); + ((unsigned char *)dst)[2] = (val >> 40); + ((unsigned char *)dst)[3] = (val >> 32); + ((unsigned char *)dst)[4] = (val >> 24); + ((unsigned char *)dst)[5] = (val >> 16); + ((unsigned char *)dst)[6] = (val >> 8); + ((unsigned char *)dst)[7] = val; +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static SPH_INLINE sph_u64 +sph_dec64be(const void *src) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_LITTLE_ENDIAN + return sph_bswap64(*(const sph_u64 *)src); +#else + return *(const sph_u64 *)src; +#endif +#else + if (((SPH_UPTR)src & 7) == 0) { +#if SPH_LITTLE_ENDIAN + return sph_bswap64(*(const sph_u64 *)src); +#else + return *(const sph_u64 *)src; +#endif + } else { + return ((sph_u64)(((const unsigned char *)src)[0]) << 56) + | ((sph_u64)(((const unsigned char *)src)[1]) << 48) + | ((sph_u64)(((const unsigned char *)src)[2]) << 40) + | ((sph_u64)(((const unsigned char *)src)[3]) << 32) + | ((sph_u64)(((const unsigned char *)src)[4]) << 24) + | ((sph_u64)(((const unsigned char *)src)[5]) << 16) + | ((sph_u64)(((const unsigned char *)src)[6]) << 8) + | (sph_u64)(((const unsigned char *)src)[7]); + } +#endif +#else + return ((sph_u64)(((const unsigned char *)src)[0]) << 56) + | ((sph_u64)(((const unsigned char *)src)[1]) << 48) + | ((sph_u64)(((const unsigned char *)src)[2]) << 40) + | ((sph_u64)(((const unsigned char *)src)[3]) << 32) + | ((sph_u64)(((const unsigned char *)src)[4]) << 24) + | ((sph_u64)(((const unsigned char *)src)[5]) << 16) + | ((sph_u64)(((const unsigned char *)src)[6]) << 8) + | (sph_u64)(((const unsigned char *)src)[7]); +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (big endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static SPH_INLINE sph_u64 +sph_dec64be_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return sph_bswap64(*(const sph_u64 *)src); +#elif SPH_BIG_ENDIAN + return *(const sph_u64 *)src; +#else + return ((sph_u64)(((const unsigned char *)src)[0]) << 56) + | ((sph_u64)(((const unsigned char *)src)[1]) << 48) + | ((sph_u64)(((const unsigned char *)src)[2]) << 40) + | ((sph_u64)(((const unsigned char *)src)[3]) << 32) + | ((sph_u64)(((const unsigned char *)src)[4]) << 24) + | ((sph_u64)(((const unsigned char *)src)[5]) << 16) + | ((sph_u64)(((const unsigned char *)src)[6]) << 8) + | (sph_u64)(((const unsigned char *)src)[7]); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * + * @param dst the destination buffer + * @param val the 64-bit value to encode + */ +static SPH_INLINE void +sph_enc64le(void *dst, sph_u64 val) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_BIG_ENDIAN + val = sph_bswap64(val); +#endif + *(sph_u64 *)dst = val; +#else + if (((SPH_UPTR)dst & 7) == 0) { +#if SPH_BIG_ENDIAN + val = sph_bswap64(val); +#endif + *(sph_u64 *)dst = val; + } else { + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); + ((unsigned char *)dst)[4] = (val >> 32); + ((unsigned char *)dst)[5] = (val >> 40); + ((unsigned char *)dst)[6] = (val >> 48); + ((unsigned char *)dst)[7] = (val >> 56); + } +#endif +#else + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); + ((unsigned char *)dst)[4] = (val >> 32); + ((unsigned char *)dst)[5] = (val >> 40); + ((unsigned char *)dst)[6] = (val >> 48); + ((unsigned char *)dst)[7] = (val >> 56); +#endif +} + +/** + * Encode a 64-bit value into the provided buffer (little endian convention). + * The destination buffer must be properly aligned. + * + * @param dst the destination buffer (64-bit aligned) + * @param val the value to encode + */ +static SPH_INLINE void +sph_enc64le_aligned(void *dst, sph_u64 val) +{ +#if SPH_LITTLE_ENDIAN + *(sph_u64 *)dst = val; +#elif SPH_BIG_ENDIAN + *(sph_u64 *)dst = sph_bswap64(val); +#else + ((unsigned char *)dst)[0] = val; + ((unsigned char *)dst)[1] = (val >> 8); + ((unsigned char *)dst)[2] = (val >> 16); + ((unsigned char *)dst)[3] = (val >> 24); + ((unsigned char *)dst)[4] = (val >> 32); + ((unsigned char *)dst)[5] = (val >> 40); + ((unsigned char *)dst)[6] = (val >> 48); + ((unsigned char *)dst)[7] = (val >> 56); +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * + * @param src the source buffer + * @return the decoded value + */ +static SPH_INLINE sph_u64 +sph_dec64le(const void *src) +{ +#if defined SPH_UPTR +#if SPH_UNALIGNED +#if SPH_BIG_ENDIAN + return sph_bswap64(*(const sph_u64 *)src); +#else + return *(const sph_u64 *)src; +#endif +#else + if (((SPH_UPTR)src & 7) == 0) { +#if SPH_BIG_ENDIAN +#if SPH_SPARCV9_GCC_64 && !SPH_NO_ASM + sph_u64 tmp; + + __asm__ __volatile__ ( + "ldxa [%1]0x88,%0" : "=r" (tmp) : "r" (src)); + return tmp; +/* + * Not worth it generally. + * +#elif SPH_PPC32_GCC && !SPH_NO_ASM + return (sph_u64)sph_dec32le_aligned(src) + | ((sph_u64)sph_dec32le_aligned( + (const char *)src + 4) << 32); +#elif SPH_PPC64_GCC && !SPH_NO_ASM + sph_u64 tmp; + + __asm__ __volatile__ ( + "ldbrx %0,0,%1" : "=r" (tmp) : "r" (src)); + return tmp; + */ +#else + return sph_bswap64(*(const sph_u64 *)src); +#endif +#else + return *(const sph_u64 *)src; +#endif + } else { + return (sph_u64)(((const unsigned char *)src)[0]) + | ((sph_u64)(((const unsigned char *)src)[1]) << 8) + | ((sph_u64)(((const unsigned char *)src)[2]) << 16) + | ((sph_u64)(((const unsigned char *)src)[3]) << 24) + | ((sph_u64)(((const unsigned char *)src)[4]) << 32) + | ((sph_u64)(((const unsigned char *)src)[5]) << 40) + | ((sph_u64)(((const unsigned char *)src)[6]) << 48) + | ((sph_u64)(((const unsigned char *)src)[7]) << 56); + } +#endif +#else + return (sph_u64)(((const unsigned char *)src)[0]) + | ((sph_u64)(((const unsigned char *)src)[1]) << 8) + | ((sph_u64)(((const unsigned char *)src)[2]) << 16) + | ((sph_u64)(((const unsigned char *)src)[3]) << 24) + | ((sph_u64)(((const unsigned char *)src)[4]) << 32) + | ((sph_u64)(((const unsigned char *)src)[5]) << 40) + | ((sph_u64)(((const unsigned char *)src)[6]) << 48) + | ((sph_u64)(((const unsigned char *)src)[7]) << 56); +#endif +} + +/** + * Decode a 64-bit value from the provided buffer (little endian convention). + * The source buffer must be properly aligned. + * + * @param src the source buffer (64-bit aligned) + * @return the decoded value + */ +static SPH_INLINE sph_u64 +sph_dec64le_aligned(const void *src) +{ +#if SPH_LITTLE_ENDIAN + return *(const sph_u64 *)src; +#elif SPH_BIG_ENDIAN +#if SPH_SPARCV9_GCC_64 && !SPH_NO_ASM + sph_u64 tmp; + + __asm__ __volatile__ ("ldxa [%1]0x88,%0" : "=r" (tmp) : "r" (src)); + return tmp; +/* + * Not worth it generally. + * +#elif SPH_PPC32_GCC && !SPH_NO_ASM + return (sph_u64)sph_dec32le_aligned(src) + | ((sph_u64)sph_dec32le_aligned((const char *)src + 4) << 32); +#elif SPH_PPC64_GCC && !SPH_NO_ASM + sph_u64 tmp; + + __asm__ __volatile__ ("ldbrx %0,0,%1" : "=r" (tmp) : "r" (src)); + return tmp; + */ +#else + return sph_bswap64(*(const sph_u64 *)src); +#endif +#else + return (sph_u64)(((const unsigned char *)src)[0]) + | ((sph_u64)(((const unsigned char *)src)[1]) << 8) + | ((sph_u64)(((const unsigned char *)src)[2]) << 16) + | ((sph_u64)(((const unsigned char *)src)[3]) << 24) + | ((sph_u64)(((const unsigned char *)src)[4]) << 32) + | ((sph_u64)(((const unsigned char *)src)[5]) << 40) + | ((sph_u64)(((const unsigned char *)src)[6]) << 48) + | ((sph_u64)(((const unsigned char *)src)[7]) << 56); +#endif +} + +#endif + +#endif /* Doxygen excluded block */ + +#endif diff --git a/libtests/aes.cc b/libtests/aes.cc index ad2f0dd..381148c 100644 --- a/libtests/aes.cc +++ b/libtests/aes.cc @@ -8,52 +8,86 @@ static void usage() { - std::cerr << "Usage: aes [+-]cbc { -encrypt | -decrypt }" - << " hex-key infile outfile" << std::endl; + std::cerr << "Usage: aes options hex-key infile outfile" << std::endl + << " -cbc -- disable CBC mode" << std::endl + << " +cbc -- enable CBC mode" << std::endl + << " -encrypt -- encrypt" << std::endl + << " -decrypt -- decrypt CBC mode" << std::endl + << " -zero-iv -- use zero initialization vector" << std::endl + << " -static-iv -- use static initialization vector" << std::endl + << " -no-padding -- disable padding" << std::endl + << "Options must precede key and file names." << std::endl; exit(2); } int main(int argc, char* argv[]) { - if (argc != 6) - { - usage(); - } - - char* cbc = argv[1]; - char* action = argv[2]; - char* hexkey = argv[3]; - char* infilename = argv[4]; - char* outfilename = argv[5]; - + bool encrypt = true; bool cbc_mode = true; - if (strcmp(cbc, "-cbc") == 0) - { - cbc_mode = false; - } - else if (strcmp(cbc, "+cbc") != 0) - { - usage(); - } + char* hexkey = 0; + char* infilename = 0; + char* outfilename = 0; + bool zero_iv = false; + bool static_iv = false; + bool disable_padding = false; - bool encrypt = true; - if (strcmp(action, "-decrypt") == 0) + for (int i = 1; i < argc; ++i) { - encrypt = false; + char* arg = argv[i]; + if ((arg[0] == '-') || (arg[0] == '+')) + { + if (strcmp(arg, "-cbc") == 0) + { + cbc_mode = false; + } + else if (strcmp(arg, "+cbc") == 0) + { + cbc_mode = true; + } + else if (strcmp(arg, "-decrypt") == 0) + { + encrypt = false; + } + else if (strcmp(arg, "-encrypt") == 0) + { + encrypt = true; + } + else if (strcmp(arg, "-zero-iv") == 0) + { + zero_iv = true; + } + else if (strcmp(arg, "-static-iv") == 0) + { + static_iv = true; + } + else if (strcmp(arg, "-no-padding") == 0) + { + disable_padding = true; + } + else + { + usage(); + } + } + else if (argc == i + 3) + { + hexkey = argv[i]; + infilename = argv[i+1]; + outfilename = argv[i+2]; + break; + } + else + { + usage(); + } } - else if (strcmp(action, "-encrypt") != 0) + if (outfilename == 0) { - usage(); + usage(); } unsigned int hexkeylen = (unsigned int)strlen(hexkey); unsigned int keylen = hexkeylen / 2; - if (keylen != Pl_AES_PDF::key_size) - { - std::cerr << "key length must be " << Pl_AES_PDF::key_size - << " bytes" << std::endl; - exit(2); - } FILE* infile = fopen(infilename, "rb"); if (infile == 0) @@ -69,7 +103,7 @@ int main(int argc, char* argv[]) exit(2); } - unsigned char key[Pl_AES_PDF::key_size]; + unsigned char* key = new unsigned char[keylen]; for (unsigned int i = 0; i < strlen(hexkey); i += 2) { char t[3]; @@ -82,11 +116,25 @@ int main(int argc, char* argv[]) } Pl_StdioFile* out = new Pl_StdioFile("stdout", outfile); - Pl_AES_PDF* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, key); + Pl_AES_PDF* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, key, keylen); + delete [] key; + key = 0; if (! cbc_mode) { aes->disableCBC(); } + if (zero_iv) + { + aes->useZeroIV(); + } + else if (static_iv) + { + aes->useStaticIV(); + } + if (disable_padding) + { + aes->disablePadding(); + } // 16 < buffer size, buffer_size is not a multiple of 8 for testing unsigned char buf[83]; diff --git a/libtests/build.mk b/libtests/build.mk index 6464502..7a53595 100644 --- a/libtests/build.mk +++ b/libtests/build.mk @@ -12,7 +12,8 @@ BINS_libtests = \ png_filter \ pointer_holder \ qutil \ - rc4 + rc4 \ + sha2 TARGETS_libtests = $(foreach B,$(BINS_libtests),libtests/$(OUTPUT_DIR)/$(call binname,$(B))) diff --git a/libtests/qtest/sha2.test b/libtests/qtest/sha2.test new file mode 100644 index 0000000..34d668f --- /dev/null +++ b/libtests/qtest/sha2.test @@ -0,0 +1,18 @@ +#!/usr/bin/env perl +require 5.008; +BEGIN { $^W = 1; } +use strict; + +chdir("sha2") or die "chdir testdir failed: $!\n"; + +require TestDriver; + +my $td = new TestDriver('sha2'); + +$td->runtest("sha2", + {$td->COMMAND => "sha2"}, + {$td->FILE => "sha2.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + +$td->report(1); diff --git a/libtests/qtest/sha2/sha2.out b/libtests/qtest/sha2/sha2.out new file mode 100644 index 0000000..702264f --- /dev/null +++ b/libtests/qtest/sha2/sha2.out @@ -0,0 +1,9 @@ +256 short: passed +256 long: passed +256 million: passed +384 short: passed +384 long: passed +384 million: passed +512 short: passed +512 long: passed +512 million: passed diff --git a/libtests/sha2.cc b/libtests/sha2.cc new file mode 100644 index 0000000..2b9aac3 --- /dev/null +++ b/libtests/sha2.cc @@ -0,0 +1,69 @@ +#include +#include +#include +#include +#include + +static void test(Pl_SHA2& sha2, char const* description, int bits, + char const* input, std::string const& output) +{ + sha2.resetBits(bits); + sha2.write((unsigned char*) input, strlen(input)); + sha2.finish(); + std::cout << description << ": "; + if (output == sha2.getHexDigest()) + { + std::cout << "passed\n"; + } + else + { + std::cout << "failed\n" + << " expected: " << output << "\n" + << " actual: " << sha2.getHexDigest() << "\n"; + } +} + +int main( int argc, char *argv[] ) +{ + Pl_SHA2 sha2; + char million_a[1000001]; + memset(million_a, 'a', 1000000); + million_a[1000000] = '\0'; + test(sha2, "256 short", 256, + "abc", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"); + test(sha2, "256 long", 256, + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"); + test(sha2, "256 million", 256, + million_a, + "cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"); + test(sha2, "384 short", 384, + "abc", + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163" + "1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7"); + test(sha2, "384 long", 384, + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "09330c33f71147e83d192fc782cd1b4753111b173b3b05d2" + "2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039"); + test(sha2, "384 million", 384, + million_a, + "9d0e1809716474cb086e834e310a4a1ced149e9c00f24852" + "7972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985"); + test(sha2, "512 short", 512, + "abc", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a" + "2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"); + test(sha2, "512 long", 512, + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu", + "8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018" + "501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"); + test(sha2, "512 million", 512, + million_a, + "e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa973eb" + "de0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217ad8cc09b"); + + return 0; +} diff --git a/make_dist b/make_dist index fb1436e..de6d3f1 100755 --- a/make_dist +++ b/make_dist @@ -119,6 +119,7 @@ if ($run_tests) { cd($srcdir); run("./configure"); + run("make -j8"); run("make check"); cd($pwd); } diff --git a/make_windows_releases b/make_windows_releases index dff7931..2ea0dbe 100755 --- a/make_windows_releases +++ b/make_windows_releases @@ -12,9 +12,11 @@ PATH=$cwd/libqpdf/build:$PATH rm -rf install-mingw* install-msvc* ./config-mingw64 +make -j8 make check install make distclean ./config-mingw32 +make -j8 make check install make distclean diff --git a/make_windows_releases-msvc b/make_windows_releases-msvc index 40a3979..702ab83 100755 --- a/make_windows_releases-msvc +++ b/make_windows_releases-msvc @@ -17,5 +17,6 @@ cwd=`pwd` PATH=$cwd/libqpdf/build:$PATH ./config-msvc $w +make -j8 make check install make distclean diff --git a/manual/qpdf-manual.xml b/manual/qpdf-manual.xml index 0bdc32c..b5e2251 100644 --- a/manual/qpdf-manual.xml +++ b/manual/qpdf-manual.xml @@ -5,8 +5,8 @@ - - + + ]> @@ -16,7 +16,7 @@ JayBerkenbilt - 2005–2012 + 2005–2013 Jay Berkenbilt @@ -402,8 +402,8 @@ make The value for - may be 40 - or 128. The restriction flags are dependent upon key length. + may be 40, + 128, or 256. The restriction flags are dependent upon key length. When no additional restrictions are given, the default is to be fully permissive. @@ -565,6 +565,44 @@ make + If is 256, + the minimum PDF version is 1.7 with extension level 8, and the + AES-based encryption format used is the PDF 2.0 encryption method + supported by Acrobat X. the same options are available as with + 128 bits with the following exceptions: + + + + + + This option is not available with 256-bit keys. AES is always + used with 256-bit encryption keys. + + + + + + + + This option is not available with 256 keys. + + + + + + + + If specified, qpdf sets the minimum version to 1.7 at + extension level 3 and writes the deprecated encryption format + used by Acrobat version IX. This option should not be used in + practice to generate PDF files that will be in general use, + but it can be useful to generate files if you are trying to + test proper support in another application for PDF files + encrypted in this way. + + + + The default for each permission option is to be fully permissive. @@ -796,6 +834,16 @@ outfile.pdf will automatically increase the version as needed when adding features that require newer PDF readers. + + The version number may be expressed in the form + major.minor.extension-level, in + which case the version is interpreted as + major.minor at extension level + extension-level. For example, + version 1.7.8 represents version 1.7 at + extension level 8. Note that minimal syntax checking is done + on the command line. + @@ -804,14 +852,19 @@ outfile.pdf This option forces the PDF version to be the exact version specified even when the file may have content that - is not supported in that version. In some cases, - forcing the output file's PDF version to be lower than that of - the input file will cause qpdf to disable certain features of - the document. Specifically, AES encryption is disabled if the - version is less than 1.6, cleartext metadata and object - streams are disabled if less than 1.5, 128-bit encryption keys - are disabled if less than 1.4, and all encryption is disabled - if less than 1.3. Even with these precautions, qpdf won't be + is not supported in that version. The version + number is interpreted in the same way as with + so that extension levels can be + set. In some cases, forcing the output file's PDF version to + be lower than that of the input file will cause qpdf to + disable certain features of the document. Specifically, + 256-bit keys are disabled if the version is less than 1.7 with + extension level 8 (except R5 is disabled if less than 1.7 with + extension level 3), AES encryption is disabled if the version + is less than 1.6, cleartext metadata and object streams are + disabled if less than 1.5, 128-bit encryption keys are + disabled if less than 1.4, and all encryption is disabled if + less than 1.3. Even with these precautions, qpdf won't be able to do things like eliminate use of newer image compression schemes, transparency groups, or other features that may have been added in more recent versions of PDF. @@ -1592,6 +1645,31 @@ outfile.pdf QPDFWriter when it rewrites encrypted files. + + When copying encrypted files, unless otherwise directed, qpdf will + preserve any encryption in force in the original file. qpdf can + do this with either the user or the owner password. There is no + difference in capability based on which password is used. When 40 + or 128 bit encryption keys are used, the user password can be + recovered with the owner password. With 256 keys, the user and + owner passwords are used independently to encrypt the actual + encryption key, so while either can be used, the owner password + can no longer be used to recover the user password. + + + Starting with version 4.0.0, qpdf can read files that are not + encrypted but that contain encrypted attachments, but it cannot + write such files. qpdf also requires the password to be specified + in order to open the file, not just to extract attachments, since + once the file is open, all decryption is handled transparently. + When copying files like this while preserving encryption, qpdf + will apply the file's encryption to everything in the file, not + just to the attachments. When decrypting the file, qpdf will + decrypt the attachments. In general, when copying PDF files with + multiple encryption formats, qpdf will choose the newest format. + The only exception to this is that clear-text metadata will be + preserved as clear-text if it is that way in the original file. + Adding and Removing Pages @@ -2383,6 +2461,179 @@ print "\n"; + 4.0.0: December 31, 2012 + + + + + Major enhancement: support has been added for newer encryption + schemes supported by version X of Adobe Acrobat. This + includes use of 127-character passwords, 256-bit encryption + keys, and the encryption scheme specified in ISO 32000-2, the + PDF 2.0 specification. This scheme can be chosen from the + command line by specifying use of 256-bit keys. qpdf also + supports the deprecated encryption method used by Acrobat IX. + This encryption style has known security weaknesses and should + not be used in practice. However, such files exist “in + the wild,” so support for this scheme is still useful. + New methods + QPDFWriter::setR6EncryptionParameters + (for the PDF 2.0 scheme) and + QPDFWriter::setR5EncryptionParameters + (for the deprecated scheme) have been added to enable these + new encryption schemes. Corresponding functions have been + added to the C API as well. + + + + + Full support for Adobe extension levels in PDF version + information. Starting with PDF version 1.7, corresponding to + ISO 32000, Adobe adds new functionality by increasing the + extension level rather than increasing the version. This + support includes addition of the + QPDF::getExtensionLevel method for + retrieving the document's extension level, addition of + versions of + QPDFWriter::setMinimumPDFVersion and + QPDFWriter::forcePDFVersion that accept + an extension level, and extended syntax for specifying forced + and minimum versions on the command line as described in . Corresponding + functions have been added to the C API as well. + + + + + Minor fixes to prevent qpdf from referencing objects in the + file that are not referenced in the file's overall structure. + Most files don't have any such objects, but some files have + contain unreferenced objects with errors, so these fixes + prevent qpdf from needlessly rejecting or complaining about + such objects. + + + + + Add new generalized methods for reading and writing files + from/to programmer-defined sources. The method + QPDF::processInputSource allows the + programmer to use any input source for the input file, and + QPDFWriter::setOutputPipeline allows the + programmer to write the output file through any pipeline. + These methods would make it possible to perform any number of + specialized operations, such as accessing external storage + systems, creating bindings for qpdf in other programming + languages that have their own I/O systems, etc. + + + + + Add new method QPDF::getEncryptionKey for + retrieving the underlying encryption key used in the file. + + + + + This release includes a small handful of non-compatible API + changes. While effort is made to avoid such changes, all the + non-compatible API changes in this version were to parts of + the API that would likely never be used outside the library + itself. In all cases, the altered methods or structures were + parts of the QPDF that were public to + enable them to be called from either + QPDFWriter or were part of validation + code that was over-zealous in reporting problems in parts of + the file that would not ordinarily be referenced. In no case + did any of the removed methods do anything worse that falsely + report error conditions in files that were broken in ways that + didn't matter. The following public parts of the + QPDF class were changed in a + non-compatible way: + + + + Updated nested QPDF::EncryptionData + class to add fields needed by the newer encryption formats, + member variables changed to private so that future changes + will not require breaking backward compatibility. + + + + + Added additional parameters to + compute_data_key, which is used by + QPDFWriter to compute the encryption + key used to encrypt a specific object. + + + + + Removed the method + flattenScalarReferences. This method + was previously used prior to writing a new PDF file, but it + has the undesired side effect of causing qpdf to read + objects in the file that were not referenced. Some + otherwise files have unreferenced objects with errors in + them, so this could cause qpdf to reject files that would + be accepted by virtually all other PDF readers. In fact, + qpdf relied on only a very small part of what + flattenScalarReferences did, so only this part has been + preserved, and it is now done directly inside + QPDFWriter. + + + + + Removed the method decodeStreams. + This method was used by the option + of the qpdf command-line tool to force + all streams in the file to be decoded, but it also suffered + from the problem of opening otherwise unreferenced streams + and thus could report false positive. The + option now causes qpdf to go + through all the motions of writing a new file based on the + original one, so it will always reference and check exactly + those parts of a file that any ordinary viewer would check. + + + + + Removed the method + trimTrailerForWrite. This method was + used by QPDFWriter to modify the + original QPDF object by removing fields from the trailer + dictionary that wouldn't apply to the newly written file. + This functionality, though generally harmless, was a poor + implementation and has been replaced by having QPDFWriter + filter these out when copying the trailer rather than + modifying the original QPDF object. (Note that qpdf never + modifies the original file itself.) + + + + + + + + Allow the PDF header to appear anywhere in the first 1024 + bytes of the file. This is consistent with what other readers + do. + + + + + Fix the pkg-config files to list zlib and + pcre in Requires.private to better + support static linking using pkg-config. + + + + + + + + 3.0.2: September 6, 2012 @@ -3274,4 +3525,16 @@ print "\n"; + + Upgrading to 4.0 + + While version 4.0 includes a few non-compatible API changes, it is + very unlikely that anyone's code would have used any of those parts + of the API since they generally required information that would + only be available inside the library. In the unlikely event that + you should run into trouble, please see the ChangeLog. See also + for a complete list of the + non-compatible API changes made in this version. + + diff --git a/qpdf.spec b/qpdf.spec index 3f32939..bf434e8 100644 --- a/qpdf.spec +++ b/qpdf.spec @@ -1,6 +1,6 @@ Summary: Command-line tools and library for transforming PDF files Name: qpdf -Version: 3.0.2 +Version: 4.0.0 Release: 1%{?dist} License: Artistic Group: System Environment/Libraries diff --git a/qpdf/qpdf-ctest.c b/qpdf/qpdf-ctest.c index 072e5a7..58fcb00 100644 --- a/qpdf/qpdf-ctest.c +++ b/qpdf/qpdf-ctest.c @@ -106,6 +106,10 @@ static void test01(char const* infile, { qpdf_read(qpdf, infile, password); printf("version: %s\n", qpdf_get_pdf_version(qpdf)); + if (qpdf_get_pdf_extension_level(qpdf) > 0) + { + printf("extension level: %d\n", qpdf_get_pdf_extension_level(qpdf)); + } printf("linearized: %d\n", qpdf_is_linearized(qpdf)); printf("encrypted: %d\n", qpdf_is_encrypted(qpdf)); if (qpdf_is_encrypted(qpdf)) @@ -304,7 +308,7 @@ static void test14(char const* infile, qpdf_read(qpdf, infile, password); qpdf_init_write(qpdf, outfile); qpdf_set_static_ID(qpdf, QPDF_TRUE); - qpdf_set_minimum_pdf_version(qpdf, "1.6"); + qpdf_set_minimum_pdf_version_and_extension(qpdf, "1.7", 8); qpdf_write(qpdf); qpdf_init_write(qpdf, outfile2); qpdf_set_static_ID(qpdf, QPDF_TRUE); @@ -374,6 +378,38 @@ static void test16(char const* infile, report_errors(); } +static void test17(char const* infile, + char const* password, + char const* outfile, + char const* outfile2) +{ + qpdf_read(qpdf, infile, password); + qpdf_init_write(qpdf, outfile); + qpdf_set_static_ID(qpdf, QPDF_TRUE); + qpdf_set_static_aes_IV(qpdf, QPDF_TRUE); + qpdf_set_r5_encryption_parameters( + qpdf, "user3", "owner3", QPDF_TRUE, QPDF_TRUE, + qpdf_r3p_low, qpdf_r3m_all, QPDF_TRUE); + qpdf_write(qpdf); + report_errors(); +} + +static void test18(char const* infile, + char const* password, + char const* outfile, + char const* outfile2) +{ + qpdf_read(qpdf, infile, password); + qpdf_init_write(qpdf, outfile); + qpdf_set_static_ID(qpdf, QPDF_TRUE); + qpdf_set_static_aes_IV(qpdf, QPDF_TRUE); + qpdf_set_r6_encryption_parameters( + qpdf, "user4", "owner4", QPDF_TRUE, QPDF_TRUE, + qpdf_r3p_low, qpdf_r3m_all, QPDF_TRUE); + qpdf_write(qpdf); + report_errors(); +} + int main(int argc, char* argv[]) { char* p = 0; @@ -430,6 +466,8 @@ int main(int argc, char* argv[]) (n == 14) ? test14 : (n == 15) ? test15 : (n == 16) ? test16 : + (n == 17) ? test17 : + (n == 18) ? test18 : 0); if (fn == 0) diff --git a/qpdf/qpdf.cc b/qpdf/qpdf.cc index 2835a96..5a4c408 100644 --- a/qpdf/qpdf.cc +++ b/qpdf/qpdf.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -96,7 +97,7 @@ Note that -- terminates parsing of encryption flags.\n\ Either or both of the user password and the owner password may be\n\ empty strings.\n\ \n\ -key-length may be 40 or 128\n\ +key-length may be 40, 128, or 256\n\ \n\ Additional flags are dependent upon key length.\n\ \n\ @@ -117,6 +118,11 @@ Additional flags are dependent upon key length.\n\ --use-aes=[yn] indicates whether to use AES encryption\n\ --force-V4 forces use of V=4 encryption handler\n\ \n\ + If 256, options are the same as 128 with these exceptions:\n\ + --force-V4 this option is not available with 256-bit keys\n\ + --use-aes this option is always on with 256-bit keys\n\ + --force-R5 forces use of deprecated R=5 encryption\n\ +\n\ print-opt may be:\n\ \n\ full allow full printing\n\ @@ -188,6 +194,9 @@ familiar with the PDF file format or who are PDF developers.\n\ --min-version=version sets the minimum PDF version of the output file\n\ --force-version=version forces this to be the PDF version of the output file\n\ \n\ +Version numbers may be expressed as major.minor.extension-level, so 1.7.3\n\ +means PDF version 1.7 at extension level 3.\n\ +\n\ Values for stream data options:\n\ \n\ compress recompress stream data when possible (default)\n\ @@ -279,6 +288,9 @@ static std::string show_encryption_method(QPDF::encryption_method_e method) case QPDF::e_aes: result = "AESv2"; break; + case QPDF::e_aesv3: + result = "AESv3"; + break; // no default so gcc will warn for missing case } return result; @@ -481,7 +493,8 @@ parse_encrypt_options( bool& r2_print, bool& r2_modify, bool& r2_extract, bool& r2_annotate, bool& r3_accessibility, bool& r3_extract, qpdf_r3_print_e& r3_print, qpdf_r3_modify_e& r3_modify, - bool& force_V4, bool& cleartext_metadata, bool& use_aes) + bool& force_V4, bool& cleartext_metadata, bool& use_aes, + bool& force_R5) { if (cur_arg + 3 >= argc) { @@ -498,9 +511,14 @@ parse_encrypt_options( { keylen = 128; } + else if (len_str == "256") + { + keylen = 256; + use_aes = true; + } else { - usage("encryption key length must be 40 or 128"); + usage("encryption key length must be 40, 128, or 256"); } while (1) { @@ -732,15 +750,30 @@ parse_encrypt_options( { usage("--force-V4 does not take a parameter"); } - if (keylen == 40) + if (keylen != 128) { - usage("--force-V4 is invalid for 40-bit keys"); + usage("--force-V4 is invalid only for 128-bit keys"); } else { force_V4 = true; } } + else if (strcmp(arg, "force-R5") == 0) + { + if (parameter) + { + usage("--force-R5 does not take a parameter"); + } + if (keylen != 256) + { + usage("--force-R5 is invalid only for 256-bit keys"); + } + else + { + force_R5 = true; + } + } else if (strcmp(arg, "use-aes") == 0) { if (parameter == 0) @@ -761,10 +794,16 @@ parse_encrypt_options( { usage("invalid -use-aes parameter"); } - if (keylen == 40) + if ((keylen == 40) && result) { usage("use-aes is invalid for 40-bit keys"); } + else if ((keylen == 256) && (! result)) + { + // qpdf would happily create files encrypted with RC4 + // using /V=5, but Adobe reader can't read them. + usage("use-aes can't be disabled with 256-bit keys"); + } else { use_aes = result; @@ -837,6 +876,21 @@ QPDFPageData::QPDFPageData(QPDF* qpdf, char const* range) : this->selected_pages = parse_numrange(range, (int)this->orig_pages.size()); } +static void parse_version(std::string const& full_version_string, + std::string& version, int& extension_level) +{ + PointerHolder vp(true, QUtil::copy_string(full_version_string)); + char* v = vp.getPointer(); + char* p1 = strchr(v, '.'); + char* p2 = (p1 ? strchr(1 + p1, '.') : 0); + if (p2 && *(p2 + 1)) + { + *p2++ = '\0'; + extension_level = atoi(p2); + } + version = v; +} + int main(int argc, char* argv[]) { whoami = QUtil::getWhoami(argv[0]); @@ -862,7 +916,7 @@ int main(int argc, char* argv[]) // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 std::cout << whoami << " version " << QPDF::QPDFVersion() << std::endl - << "Copyright (c) 2005-2012 Jay Berkenbilt" + << "Copyright (c) 2005-2013 Jay Berkenbilt" << std::endl << "This software may be distributed under the terms of version 2 of the" << std::endl @@ -902,6 +956,7 @@ int main(int argc, char* argv[]) qpdf_r3_print_e r3_print = qpdf_r3p_full; qpdf_r3_modify_e r3_modify = qpdf_r3m_all; bool force_V4 = false; + bool force_R5 = false; bool cleartext_metadata = false; bool use_aes = false; @@ -985,7 +1040,7 @@ int main(int argc, char* argv[]) user_password, owner_password, keylen, r2_print, r2_modify, r2_extract, r2_annotate, r3_accessibility, r3_extract, r3_print, r3_modify, - force_V4, cleartext_metadata, use_aes); + force_V4, cleartext_metadata, use_aes, force_R5); encrypt = true; decrypt = false; copy_encryption = false; @@ -1369,8 +1424,14 @@ int main(int argc, char* argv[]) std::cout << "checking " << infilename << std::endl; try { - std::cout << "PDF Version: " << pdf.getPDFVersion() - << std::endl; + int extension_level = pdf.getExtensionLevel(); + std::cout << "PDF Version: " << pdf.getPDFVersion(); + if (extension_level > 0) + { + std::cout << " extension level " + << pdf.getExtensionLevel(); + } + std::cout << std::endl; ::show_encryption(pdf); if (pdf.isLinearized()) { @@ -1381,12 +1442,14 @@ int main(int argc, char* argv[]) else { std::cout << "File is not linearized\n"; - // calling flattenScalarReferences causes full - // traversal of file, so any structural errors - // would be exposed. - pdf.flattenScalarReferences(); - // Also explicitly decode all streams. - pdf.decodeStreams(); + // Write the file no nowhere, uncompressing + // streams. This causes full file traversal + // and decoding of all streams we can decode. + QPDFWriter w(pdf); + Pl_Discard discard; + w.setOutputPipeline(&discard); + w.setStreamDataMode(qpdf_s_uncompress); + w.write(); okay = true; } } @@ -1585,6 +1648,23 @@ int main(int argc, char* argv[]) r3_accessibility, r3_extract, r3_print, r3_modify); } } + else if (keylen == 256) + { + if (force_R5) + { + w.setR5EncryptionParameters( + user_password.c_str(), owner_password.c_str(), + r3_accessibility, r3_extract, r3_print, r3_modify, + !cleartext_metadata); + } + else + { + w.setR6EncryptionParameters( + user_password.c_str(), owner_password.c_str(), + r3_accessibility, r3_extract, r3_print, r3_modify, + !cleartext_metadata); + } + } else { throw std::logic_error("bad encryption keylen"); @@ -1600,11 +1680,17 @@ int main(int argc, char* argv[]) } if (! min_version.empty()) { - w.setMinimumPDFVersion(min_version); + std::string version; + int extension_level = 0; + parse_version(min_version, version, extension_level); + w.setMinimumPDFVersion(version, extension_level); } if (! force_version.empty()) { - w.forcePDFVersion(force_version); + std::string version; + int extension_level = 0; + parse_version(force_version, version, extension_level); + w.forcePDFVersion(version, extension_level); } w.write(); } diff --git a/qpdf/qpdf.testcov b/qpdf/qpdf.testcov index f966337..a0578f2 100644 --- a/qpdf/qpdf.testcov +++ b/qpdf/qpdf.testcov @@ -29,8 +29,7 @@ QPDF lin outlines in part 1 QPDF lin nshared_total > nshared_first_page 1 QPDF lin part 8 empty 1 QPDF lin check shared past first page 0 -QPDF opt flatten array scalar 0 -QPDF opt flatten dict scalar 0 +QPDFWriter flatten array null 0 main QTest implicit 0 main QTest indirect 1 main QTest null 0 @@ -117,7 +116,6 @@ qpdf unable to filter 0 QPDF_String non-trivial UTF-16 0 QPDF xref overwrite object 0 QPDF decoding error warning 0 -QPDF_Stream ignore non-dictionary DecodeParms 0 qpdf-c called qpdf_init 0 qpdf-c called qpdf_cleanup 0 qpdf-c called qpdf_more_warnings 0 @@ -152,7 +150,7 @@ qpdf-c called qpdf_allow_modify_form 0 qpdf-c called qpdf_allow_modify_annotation 0 qpdf-c called qpdf_allow_modify_other 0 qpdf-c called qpdf_allow_modify_all 0 -QPDFWriter increasing minimum version 0 +QPDFWriter increasing minimum version 1 QPDFWriter using forced PDF version 0 qpdf-c called qpdf_set_minimum_pdf_version 0 qpdf-c called qpdf_force_pdf_version 0 @@ -242,3 +240,22 @@ QPDF_Tokenizer EOF reading token 0 QPDF_Tokenizer EOF reading appendable token 0 QPDFWriter extra header text no newline 0 QPDFWriter extra header text add newline 0 +QPDF bogus 0 offset 0 +QPDF global offset 0 +QPDFWriter make stream key direct 0 +QPDFWriter copy V5 0 +QPDFWriter increasing extension level 0 +QPDFWriter make Extensions direct 0 +QPDFWriter make ADBE direct 1 +QPDFWriter preserve Extensions 0 +QPDFWriter create Extensions 1 +QPDFWriter remove ADBE 0 +QPDFWriter remove existing Extensions 0 +QPDFWriter preserve ADBE 0 +QPDF_encryption skip 0x28 0 +QPDF_encrypt crypt array 0 +QPDF_encryption CFM AESV3 0 +QPDFWriter remove Crypt 0 +qpdf-c called qpdf_get_pdf_extension_level 0 +qpdf-c called qpdf_set_r5_encryption_parameters 0 +qpdf-c called qpdf_set_r6_encryption_parameters 0 diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index d12f064..bf62cee 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -111,6 +111,55 @@ $td->runtest("new stream", show_ntests(); # ---------- +$td->notify("--- Extensions Dictionary Tests ---"); +my @ext_inputs = ('minimal.pdf', 'extensions-adbe.pdf', + 'extensions-other.pdf', 'extensions-adbe-other.pdf'); +my @new_versions = ('1.3', '1.6', '1.7.1', '1.7.2', '1.7.3', + '1.8', '1.8.0', '1.8.2', '1.8.5'); +$n_tests += (4 * @new_versions + 3) * @ext_inputs; +foreach my $input (@ext_inputs) +{ + my $base = $input; + $base =~ s/\.pdf$//; + if ($base eq 'minimal') + { + $base = 'extensions-none'; + } + foreach my $version (@new_versions) + { + foreach my $op (qw(min force)) + { + $td->runtest("$input: $op version to $version", + {$td->COMMAND => + "qpdf --static-id" . + " --$op-version=$version $input a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); + $td->runtest("check version information ($op $version)", + {$td->COMMAND => "test_driver 34 a.pdf"}, + {$td->FILE => "$base-$op-$version.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + if (($op eq 'force') && ($version eq '1.8.5')) + { + # Look at the actual file for a few cases to make sure + # qdf and non-qdf output are okay + $td->runtest("check file", + {$td->FILE => "a.pdf"}, + {$td->FILE => "$base-$op-$version.pdf"}); + $td->runtest("$input: $op version to $version", + {$td->COMMAND => + "qpdf --qdf --static-id" . + " --$op-version=$version $input a.qdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); + $td->runtest("check file", + {$td->FILE => "a.qdf"}, + {$td->FILE => "$base-$op-$version.qdf"}); + } + } + } +} +show_ntests(); +# ---------- $td->notify("--- Page API Tests ---"); $n_tests += 9; @@ -147,9 +196,10 @@ $td->runtest("remove page we don't have", {$td->COMMAND => "test_driver 22 page_api_1.pdf"}, {$td->FILE => "page_api_1.out2", $td->EXIT_STATUS => 2}, $td->NORMALIZE_NEWLINES); +show_ntests(); # ---------- $td->notify("--- Miscellaneous Tests ---"); -$n_tests += 53; +$n_tests += 57; $td->runtest("qpdf version", {$td->COMMAND => "qpdf --version"}, @@ -265,8 +315,8 @@ $td->runtest("C API: min/force versions", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); $td->runtest("C check version 1", - {$td->COMMAND => "qpdf --check a.pdf"}, - {$td->FILE => "min-version.out", + {$td->COMMAND => "qpdf-ctest 1 a.pdf '' ''"}, + {$td->FILE => "c-min-version.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); $td->runtest("C check version 2", @@ -403,6 +453,21 @@ $td->runtest("check output", $td->runtest("check output", {$td->FILE => "d.pdf"}, {$td->FILE => "extra-header-lin-newline.pdf"}); +$td->runtest("output to custom pipeline", + {$td->COMMAND => "test_driver 33 minimal.pdf"}, + {$td->STRING => "test 33 done\n", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("check output", + {$td->FILE => "a.pdf"}, + {$td->FILE => "custom-pipeline.pdf"}); +$td->runtest("object with zero offset", + {$td->COMMAND => "qpdf --check zero-offset.pdf"}, + {$td->FILE => "zero-offset.out", $td->EXIT_STATUS => 3}, + $td->NORMALIZE_NEWLINES); +$td->runtest("check file with leading junk", + {$td->COMMAND => "qpdf --check leading-junk.pdf"}, + {$td->FILE => "leading-junk.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); show_ntests(); # ---------- @@ -451,6 +516,7 @@ foreach my $d (@nrange_tests) $td->NORMALIZE_NEWLINES); } +show_ntests(); # ---------- $td->notify("--- Merging and Splitting ---"); $n_tests += 6; @@ -498,6 +564,7 @@ $td->runtest("avoid respecification of password", $td->runtest("check output", {$td->FILE => "a.pdf"}, {$td->FILE => "pages-copy-encryption.pdf"}); +show_ntests(); # ---------- $td->notify("--- PDF From Scratch ---"); $n_tests += 2; @@ -509,6 +576,7 @@ $td->runtest("basic qpdf from scratch", $td->runtest("check output", {$td->FILE => "a.pdf"}, {$td->FILE => "from-scratch-0.pdf"}); +show_ntests(); # ---------- $td->notify("--- Copy Foreign Objects ---"); $n_tests += 7; @@ -531,6 +599,7 @@ $td->runtest("copy objects error", {$td->FILE => "copy-foreign-objects-errors.out", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +show_ntests(); # ---------- $td->notify("--- Error Condition Tests ---"); # $n_tests incremented after initialization of badfiles below. @@ -863,7 +932,7 @@ for (my $n = 16; $n <= 19; ++$n) show_ntests(); # ---------- $td->notify("--- Specific File Tests ---"); -$n_tests += 4; +$n_tests += 5; $n_compare_pdfs += 1; # Special PDF files that caused problems at some point @@ -892,6 +961,10 @@ $td->runtest("damaged stream", {$td->COMMAND => "qpdf --check damaged-stream.pdf"}, {$td->FILE => "damaged-stream.out", $td->EXIT_STATUS => 3}, $td->NORMALIZE_NEWLINES); +$td->runtest("damaged stream (C)", + {$td->COMMAND => "qpdf-ctest 2 damaged-stream.pdf '' a.pdf"}, + {$td->FILE => "damaged-stream-c-check.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); show_ntests(); # ---------- @@ -1121,9 +1194,15 @@ foreach my $base (@to_linearize) foreach my $omode (qw(disable preserve generate)) { my $oarg = "-object-streams=$omode"; + my $sdarg = ""; + if (($base eq 'lin-special') || ($base eq 'object-stream')) + { + $sdarg = "--stream-data=uncompress"; + } $td->runtest("linearize $base ($omode)", {$td->COMMAND => - "qpdf -linearize $oarg --static-id $base.pdf a.pdf"}, + "qpdf -linearize $oarg $sdarg" . + " --static-id $base.pdf a.pdf"}, {$td->STRING => "", $td->EXIT_STATUS => 0}); $td->runtest("check linearization", @@ -1140,12 +1219,12 @@ foreach my $base (@to_linearize) # the table values. $td->runtest("relinearize $base 1", {$td->COMMAND => - "qpdf -linearize --static-id a.pdf b.pdf"}, + "qpdf -linearize $sdarg --static-id a.pdf b.pdf"}, {$td->STRING => "", $td->EXIT_STATUS => 0}); $td->runtest("relinearize $base 2", {$td->COMMAND => - "qpdf -linearize --static-id b.pdf c.pdf"}, + "qpdf -linearize $sdarg --static-id b.pdf c.pdf"}, {$td->STRING => "", $td->EXIT_STATUS => 0}); $td->runtest("compare files ($omode)", @@ -1171,6 +1250,10 @@ $td->notify("--- Encryption Tests ---"); # resulting files were saved and manually checked with Acrobat 5.0 to # ensure that the security settings were as intended. +# The enc-XI-file.pdf files were treated the same way but with Acrobat +# XI instead of Acrobat 5.0. They were used to create test files with +# newer encryption formats. + # Values: basename, password, encryption flags, /P Encrypt key, # extract-for-accessibility, extract-for-any-purpose, # print-low-res, print-high-res, modify-assembly, modify-forms, @@ -1214,9 +1297,25 @@ my @encrypted_files = '', -4, 1, 1, 1, 1, 1, 1, 1, 1, 1], ['long-password', 'asdf asdf asdf asdf asdf asdf qwer'], - ['long-password', 'asdf asdf asdf asdf asdf asdf qw']); + ['long-password', 'asdf asdf asdf asdf asdf asdf qw'], + ['XI-base', ''], + ['XI-R6,V5,O=master', '', + '-extract=n -print=none -modify=assembly', -2368, + 1, 0, 0, 0, 1, 0, 0, 0, 0], + ['XI-R6,V5,O=master', 'master', + '-extract=n -print=none -modify=assembly', -2368, + 1, 0, 0, 0, 1, 0, 0, 0, 0], + ['XI-R6,V5,U=view,O=master', 'view', + '-print=low', -2052, + 1, 1, 1, 0, 1, 1, 1, 1, 1], + ['XI-R6,V5,U=view,O=master', 'master', + '-print=low', -2052, + 1, 1, 1, 0, 1, 1, 1, 1, 1], + ['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnm'], + ['XI-long-password', 'qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcv'], + ); -$n_tests += 3 + (2 * (@encrypted_files)) + (6 * (@encrypted_files - 3)) + 9; +$n_tests += 5 + (2 * (@encrypted_files)) + (6 * (@encrypted_files - 6)) + 9; $td->runtest("encrypted file", {$td->COMMAND => "test_driver 2 U25A0.pdf"}, @@ -1233,6 +1332,19 @@ $td->runtest("recheck encrypted file", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +# Test that long passwords that are one character too short fail. We +# test the truncation cases in the loop below by using passwords +# longer than the supported length. +$td->runtest("significant password characters (V < 5)", + {$td->COMMAND => "qpdf --check enc-long-password.pdf" . + " --password='asdf asdf asdf asdf asdf asdf q'"}, + {$td->REGEXP => ".*invalid password.*", $td->EXIT_STATUS => 2}); +$td->runtest("significant password characters (V = 5)", + {$td->COMMAND => "qpdf --check enc-XI-long-password.pdf" . + " --password=qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxc"}, + {$td->REGEXP => ".*invalid password.*", $td->EXIT_STATUS => 2}); + +my $enc_base = undef; foreach my $d (@encrypted_files) { my ($file, $pass, $xeflags, $P, @@ -1251,17 +1363,26 @@ foreach my $d (@encrypted_files) "modify annotations: " . &$f($modifyannot) . "\n" . "modify other: " . &$f($modifyother) . "\n" . "modify anything: " . &$f($modifyall) . "\n"; + if ($file =~ m/XI-/) + { + $enc_details .= + "stream encryption method: AESv3\n" . + "string encryption method: AESv3\n" . + "file encryption method: AESv3\n"; + } # Test writing to stdout $td->runtest("decrypt $file", {$td->COMMAND => - "qpdf --static-id -qdf --no-original-object-ids" . + "qpdf --static-id -qdf --object-streams=disable" . + " --no-original-object-ids" . " --password=\"$pass\" enc-$file.pdf -" . " > $file.enc"}, {$td->STRING => "", $td->EXIT_STATUS => 0}); - if ($file eq 'base') + if ($file =~ m/base$/) { + $enc_base = $file; $td->runtest("check ID", {$td->COMMAND => "perl check-ID.pl $file.enc"}, {$td->STRING => "ID okay\n", @@ -1271,20 +1392,27 @@ foreach my $d (@encrypted_files) else { $td->runtest("check against base", - {$td->COMMAND => "./diff-encrypted base.enc $file.enc"}, + {$td->COMMAND => + "./diff-encrypted $enc_base.enc $file.enc"}, {$td->STRING => "okay\n", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); } - if ($file =~ m/^R(\d),V(\d)(?:,U=(\w+))?(?:,O=(\w+))?$/) + if ($file =~ m/^(?:XI-)?R(\d),V(\d)(?:,U=(\w+))?(?:,O=(\w+))?$/) { my $R = $1; my $V = $2; my $upass = $3 || ""; my $opass = $4 || ""; - my $bits = (($V == 2) ? 128 : 40); + my $bits = (($V == 5) ? 256 : ($V == 2) ? 128 : 40); my $eflags = "-encrypt \"$upass\" \"$opass\" $bits $xeflags --"; + if (($pass ne $upass) && ($V >= 5)) + { + # V >= 5 can no longer recover user password with owner + # password. + $upass = ""; + } $td->runtest("encrypt $file", {$td->COMMAND => "qpdf --static-id --no-original-object-ids -qdf" . @@ -1344,29 +1472,52 @@ $td->runtest("C API: invalid password", $td->NORMALIZE_NEWLINES); my @cenc = ( - [11, 'hybrid-xref.pdf', "''", 'r2', ""], - [12, 'hybrid-xref.pdf', "''", 'r3', ""], - [15, 'hybrid-xref.pdf', "''", 'r4', ""], + [11, 'hybrid-xref.pdf', "''", 'r2', "", ""], + [12, 'hybrid-xref.pdf', "''", 'r3', "", ""], + [15, 'hybrid-xref.pdf', "''", 'r4', "", ""], + [17, 'hybrid-xref.pdf', "''", 'r5', "", "owner3"], + [18, 'hybrid-xref.pdf', "''", 'r6', "", "user4"], [13, 'c-r2.pdf', 'user1', 'decrypt with user', - "user password: user1\n"], + "user password: user1\n", ""], [13, 'c-r3.pdf', 'owner2', 'decrypt with owner', - "user password: user2\n"], + "user password: user2\n", ""], + [13, 'c-r5-in.pdf', 'user3', 'decrypt R5 with user', + "user password: user3\n", ""], + [13, 'c-r6-in.pdf', 'owner4', 'decrypt R6 with owner', + "user password: \n", ""], ); $n_tests += 2 * @cenc; foreach my $d (@cenc) { - my ($n, $infile, $pass, $description, $output) = @$d; + my ($n, $infile, $pass, $description, $output, $checkpass) = @$d; my $outfile = $description; $outfile =~ s/ /-/g; - $outfile = "c-$outfile.pdf"; + my $pdf_outfile = "c-$outfile.pdf"; + my $check_outfile = "c-$outfile.out"; $td->runtest("C API encryption: $description", {$td->COMMAND => "qpdf-ctest $n $infile $pass a.pdf"}, {$td->STRING => $output, $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); - $td->runtest("check $description", - {$td->FILE => "a.pdf"}, - {$td->FILE => $outfile}); + if (-f $pdf_outfile) + { + $td->runtest("check $description content", + {$td->FILE => "a.pdf"}, + {$td->FILE => $pdf_outfile}); + } + else + { + # QPDF doesn't provide any way to make the random bits in + # /Perms static, so we have no way to predictably create a + # /V=5 encrypted file. It's not worth adding this...the test + # suite is adequate without having a statically predictable + # file. + $td->runtest("check $description", + {$td->COMMAND => + "qpdf --check a.pdf --password=$checkpass"}, + {$td->FILE => $check_outfile, $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + } } # Test combinations of linearization and encryption. Note that we do @@ -1409,7 +1560,7 @@ $td->runtest("check linearization", $td->NORMALIZE_NEWLINES); # Test AES encryption in various ways. -$n_tests += 14; +$n_tests += 18; $td->runtest("encrypt with AES", {$td->COMMAND => "qpdf --encrypt '' '' 128 --use-aes=y --" . " enc-base.pdf a.pdf"}, @@ -1469,6 +1620,24 @@ $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); +$td->runtest("encrypt with V=5,R=5", + {$td->COMMAND => + "qpdf --encrypt user owner 256 --force-R5 -- " . + "minimal.pdf a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); +$td->runtest("check encryption", + {$td->COMMAND => "qpdf --check a.pdf --password=owner"}, + {$td->FILE => "V5R5.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +$td->runtest("encrypt with V=5,R=6", + {$td->COMMAND => + "qpdf --encrypt user owner 256 -- " . + "minimal.pdf a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); +$td->runtest("check encryption", + {$td->COMMAND => "qpdf --check a.pdf --password=user"}, + {$td->FILE => "V5R6.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); # Look at some actual V4 files $n_tests += 14; @@ -1550,6 +1719,53 @@ $td->runtest("compare qdf", {$td->STRING => "okay\n", $td->EXIT_STATUS => 0}, $td->NORMALIZE_NEWLINES); +# Files with attachments +my @attachments = ( + 'enc-XI-attachments-base.pdf', + 'enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf', + 'enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf'); +$n_tests += 4 * @attachments + 3; +foreach my $f (@attachments) +{ + my $pass = ''; + my $tpass = ''; + if ($f =~ m/U=([^,\.]+)/) + { + $pass = "--password=$1"; + $tpass = $1; + } + $td->runtest("decrypt $f", + {$td->COMMAND => "qpdf --decrypt $pass $f a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); + $td->runtest("extract attachments", + {$td->COMMAND => "test_driver 35 a.pdf"}, + {$td->FILE => "attachments.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + $td->runtest("copy $f", + {$td->COMMAND => "qpdf $pass $f a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); + $td->runtest("extract attachments", + {$td->COMMAND => "test_driver 35 a.pdf $tpass"}, + {$td->FILE => "attachments.out", $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +} +$td->runtest("unfilterable with crypt", + {$td->COMMAND => + "test_driver 36 unfilterable-with-crypt.pdf attachment"}, + {$td->FILE => "unfilterable-with-crypt-before.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); +unlink "a.pdf"; +$td->runtest("decrypt file", + {$td->COMMAND => "qpdf -decrypt --password=attachment" . + " unfilterable-with-crypt.pdf a.pdf"}, + {$td->STRING => "", $td->EXIT_STATUS => 0}); +$td->runtest("copy of unfilterable with crypt", + {$td->COMMAND => + "test_driver 36 a.pdf attachment"}, + {$td->FILE => "unfilterable-with-crypt-after.out", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); show_ntests(); # ---------- diff --git a/qpdf/qtest/qpdf/V5R5.out b/qpdf/qtest/qpdf/V5R5.out new file mode 100644 index 0000000..91fcd99 --- /dev/null +++ b/qpdf/qtest/qpdf/V5R5.out @@ -0,0 +1,20 @@ +checking a.pdf +PDF Version: 1.7 extension level 3 +R = 5 +P = -4 +User password = +extract for accessibility: allowed +extract for any purpose: allowed +print low resolution: allowed +print high resolution: allowed +modify document assembly: allowed +modify forms: allowed +modify annotations: allowed +modify other: allowed +modify anything: allowed +stream encryption method: AESv3 +string encryption method: AESv3 +file encryption method: AESv3 +File is not linearized +No syntax or stream encoding errors found; the file may still contain +errors that qpdf cannot detect diff --git a/qpdf/qtest/qpdf/V5R6.out b/qpdf/qtest/qpdf/V5R6.out new file mode 100644 index 0000000..fa5569a --- /dev/null +++ b/qpdf/qtest/qpdf/V5R6.out @@ -0,0 +1,20 @@ +checking a.pdf +PDF Version: 1.7 extension level 8 +R = 6 +P = -4 +User password = user +extract for accessibility: allowed +extract for any purpose: allowed +print low resolution: allowed +print high resolution: allowed +modify document assembly: allowed +modify forms: allowed +modify annotations: allowed +modify other: allowed +modify anything: allowed +stream encryption method: AESv3 +string encryption method: AESv3 +file encryption method: AESv3 +File is not linearized +No syntax or stream encoding errors found; the file may still contain +errors that qpdf cannot detect diff --git a/qpdf/qtest/qpdf/attachments.out b/qpdf/qtest/qpdf/attachments.out new file mode 100644 index 0000000..b4caa33 --- /dev/null +++ b/qpdf/qtest/qpdf/attachments.out @@ -0,0 +1,6 @@ +attachment1.txt: +This is the first attachment. +--END-- +attachment2.png: +.PNG........IHDR...1 (2620 bytes)--END-- +test 35 done diff --git a/qpdf/qtest/qpdf/c-decrypt-R5-with-user.pdf b/qpdf/qtest/qpdf/c-decrypt-R5-with-user.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e3ff67ee6134a16fd219626544cc95d535955dd9 GIT binary patch literal 9731 zcmc&)3sh9q8YT?HQ_3T8A!=<9?5f1fdCUwWD#&939}LW>ScozVM;h)hgL4iKM?;A+ z)FQ0tY1Xx_-2_tyN(o)0w+FerrD?eCT`p9v)ZClay;%9K_Bm(ZoZ3^(tOe_=1?$Y5 z*=K*>{`dcX=imSO<8w1~_u+{eEWYc?jvp}`D1p7W44XY0C^BnU+QwMyHU@BC=Vhd2 z@*mPn484?gaK8Wrd{i=WAzejV0SV^L<^B}o}O~tg80k}%M zwa8vV1H}@C9vaV&goD(=pv7w{;DB@!YqHu);mjCqE8$oY61Kt^eQ}wQEeDE)w5^nV z2&i!-Q0OdHmUh6a)x^>nw3$cC0XWJ1Z>rlGGQqVlASUIl+hc1Js%OVN);})mubY!!JGFF5*d8bT zO4@+4^X$cCc*ld0{f%wlOzJ@5S6A9=yKY@R2iA0*-JL%v|A=)$tFdYMtZ}!FK2rJJ zo1d*LYFoLd{+qWWPuJDlRePzndefI{&Mz~nE%}}A)VJ;LKN}H}xT-0q?EAW#*EZ}8 zvUa$(FZi?4x}&wBujKNPTV?ClK6CZmiqLENuq9<>%zo^t_}zsaO%FQ@wyfA)btv?K zbM<4_DYu5=pSIjs*VbLP!l`JOwu0G~&PD|$KcOjVbw7|5tbC&}yoO=3$inyfBi6LU zl=XdiV0wl*JSaAC?9t^rZtBiVI5S@J^~bjkUQj)9IxBDemY}ZA+8o#0$6nTc`a1RX zW#`|ui+euU_gs7PN7jQA>c*UU@qb%GvEXS%4+RFr@7o&V+^gE(|KH@U)p;+u8nP8B zD<;i)KRQ{v?&(pftM5FTKYi`wTz13bo3)R6hkqKl;9S3w7@hV#{&*X+{ankQ>(>La4?kS_ubN{S z;p)cYUqpS|r+jo)^MZS(-Ykj^?YX0T+mwGt4b1#7wUOG{qwjtpJ~L$&xR6qD_o0^B zA5y-%Z_)zjydGckxH0eMgHe}mue7m%gnqlgQo?`&KnOuV2mwQA_>drygo~EH;KEJ# z5Eo`77sT+wMF+eHu}4zENZ|oO7(#d+5T?e3IY|MJ^Z*I75T02)Lp-wxb5g=AlrSeH zJU|H#P@W+I0wqjc2oyrJa(ci>;+bEEXb2nBAC>`>z(lc#F0q)>>^0CP9HJU0fJRFI zI9smC0Y?tWg@I=5l@2qUn$sx;VK*CT7Er=kxLYDADJSj`gp*2G zvs|1w&L}5g17zaF`x44Y@CLa!aTY;2dEQ3y_!Gfd1mz^eiClK#EP`_KyrN~Y6K4^W zlP4pPi4$iLl#{Ib#J?U;PJ+r<%Eig6j;m$Wal)&Pt7X-3K8Zl~PA#jB z^BE1wNmd=_(<_vdtUAu;dMGDZb(~K;QBJbzIO$c#)w1e1pLU~ml2ym~GXTm-Rvjn3 z>bOQ$9Vfl&xJFhT=TBwG-f3jjasG&ia*|cY`SU8uNmd=_56>tkS#_L0$)lWP)p5$J zj%#GqalXcZ+DTR&=PM|KoV=DpJ@ryp){s_08SK0v_jonASV`oCWRG(F@|9mK4*8_dG4_05K(7v)f>gHnddna%S2yO1F^VkOT1B%Kvk8`tZLl6| zv6TWvzQr~VmYc;JIi_-0_0{1(VXQ1>`4TW!p@H9pLJdEcSPbS1IUH62Ebe6J;3&F= z1%-pPV!mQCNac5Jfct3!N;HDI1j|8;ppsT0h8OwNkon!I*nAXOT5A|yHqGU{Z$rm$-S}IM0Cm0G;mmf#a-^5h()2m(p+y?fkjjI&cvb-V7^r0(%VKWSp82lQ5SgsGi1*sE|k{OgIQGGtjGsT7Hs#tK&!L z{DG|kV%qDRPS7`~gic}?OHCmD&lPEQzrbsf0Bs5yp+&>;s_>04)xi}zjnl89D zcv|qrO7Pswh7aG*O8oz;9sjxGu2EC(JwE=U>EQL9MXrkZx%ZE`?NBpVI z))@y|=O0iXNeI*}IUH$zJ}BU=s<>+I==;k~6NWW8*cu1D3e%EW5{r+|)a$UH0dNrG AivR!s literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/c-decrypt-R6-with-owner.pdf b/qpdf/qtest/qpdf/c-decrypt-R6-with-owner.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8e5d9b81a198fd6f896e5d8636ab49b6def07694 GIT binary patch literal 9731 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 AkN^Mx literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/c-min-version.out b/qpdf/qtest/qpdf/c-min-version.out new file mode 100644 index 0000000..94558ac --- /dev/null +++ b/qpdf/qtest/qpdf/c-min-version.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 8 +linearized: 0 +encrypted: 0 diff --git a/qpdf/qtest/qpdf/c-r5-in.pdf b/qpdf/qtest/qpdf/c-r5-in.pdf new file mode 100644 index 0000000000000000000000000000000000000000..19c67a30dbf07af2c8e3e1314e9ecc707421652b GIT binary patch literal 11195 zcmc(lc~lcu8^*O27b-~I7i_1kh@hCstQnz*5Yj5EL@ArgWHNzh2$+P;PtmGXiVDc4 zf>tVSMJ$R)5x0U?+FBLd>H>nLwpw?vij~&y&I}6Q8GF9JNX~&XOYZWUckX@W-sijy zfpUe5bRuc(aO~;Uf2^et48^GCS-ZJG67NJ4t2b(6^hSugoFe!1=1)9nBOA;bxL+U% zsM@8ZFB{M5AQW1)vGy5qzkQ3CO|b$X^Ms1bsyTrT9+?rsQE!5|7eEiU%4lT z9?Ne(={dhN%AK~(z8*IFe)0X%8BM7b(hsr?>LU;B_xBpxmKqkad2P?4sf|@1yq6kj zcWP?i%H9QQ8gKsIlJjopxXm9aMo;~vIArzZ=#fXVe*C-O&ea`_#zP7u^_~g1~9;+U@Y9E}VssBT_=vGT{e(Xi~ z-LEc9EeH)m2GDI6rYf;WWR3T-yseEvryY;4uO-GEu8%n!@Bg%HlN?=nC`&yesn5?x z{+{aclfOgCp~a`O*6%x`>sy=T4?T1}-D(rRYp8$g#N_%_ntMZzjST8Jxh&-Nz?As` zo2jgyOk=A^mY#T7f-_N_g9(fNmh z{o8jx%r;$GSr}lLKjmJ3EV|YHx!;v(?Q^$f{D_8;D{i0ux;(X?ec8I${AZf1IlfDi z(`rgiW`2jI_rJf`y**-uQ%1t&#%#?HtAX{iH$MNes?q)Z{%*asGorj2mxRr~)m*UQ z4SM77Hy2Fe_T%H&>8J_$~_l?q1`bOI|eyF30}@gub4Q!2|~fB_R*@1P&kAL zQv?yF6GHM629XH|ia$t&o1hXm2~lo}g9A5rz(JUqC?=E?Dj-4;q@#k+v{Yyl75YOv zIucqzI$G%{($R|0CYXU-N0`tiCRD(L3Rp)Gp$AN8dKM%R<}#-i#xAp+e~!*T=z%(! zj1VS}Nc>o}miCNE1To=yB%}yLQV0ZE3#1L8!zfu3gbB~W-Rgwu%EVO>G2y~9TZ)Ne7BdMiz(P#?SQ0Y{qrp;4Ttvi79p5U; zj35iygX zw3cFet&S5G)p6vtI!;(r$N5Es=-m+()p34BBWALwj`PbaF_T4goL}pSnJlW~{K8Yr zWKkVQU#sJUMRlBCc8lX=Q61;^0AePK>NxsZ9VacSw(;>NxLlh~s2Y9p@dCmrNbTsq9~T z(|`9#Fa0`Y`hrv6)YeU(JKR`~ACEj*m+O=F=c3SMR=>@%e>2QJ(nZOTqaXP?tXq5E z`$GHRJs!PIJlP%W(h%!XT3Z|=^1XOB<|P*AIrCPH1f~>E2NM_8K*Mw_A%$*fNR0V;oIsol6jkl>nj{sKXL^q#U`4dSucHN z-p_f7(rpp4e*22{psQ8$i`46W^vNNT&`o&*AEdc2E1H1Y1y87H6-5Z|YG8C5*5m03 z`3@%z2Y46%(!Qu<@1AMMudP;R`jwv_N2J^=V(Z0NT|JC$!}?&`mBwx1@+C!A6PESy zO{qN;e@E+3dcSQ+`dhhARxE50V|BGLx(zF2*S8ms49fb#KXOCQdXPyC%G!s6YYZXa7` zLNQ{ol04wCdQ8L+r}%4Lg?s)?Sl_hdd-lu1yzvz$#8_rg`QVh~S<%XivBwi!hdJn_ zfwD9d(T*IPlGbapI+YlnEygm7$`{uw-%>j%Cm)kt9q-wEx##5c@*~sxC%pIVlGP(} z&sOgeW0^(e$Ny0%`L1s3*oGFjyMvDY>G)|=S>WEb%+iLURi~WEq)aiESyVnYvZ8Wn zm{ZBB=)4Ur3ah)#SM94#+I{)yEY}x-8ChX%Vl1<${E;)6zwM-4`^t@oW^GjX9@~rw z2V6)$D+LA;wcYQpAoxjs#WuD%{6`sTZ0EVHQmy25Aef4U?_ zSwF0>>UFAK625EpW<_z-h}~}6p@17`n)`LfX5 z{_`A%Kkn%_d`wl;wVIH!eJvZUTn~hZqh%JAe_HqRXSPbRCTs8l$EEs5ze}UDZ#MLK zxZ=*J+9+w@oD?yZSyaAmBfH?X_S%Pg!bT2QHTD(EY@H1~pXW%fB6F;-WV z_fqhwG)j}Y<7`TtDat#Z%f6AwW0-(g7Gw;1Df~b(p|>*lRhr<-1U;pR)~jiQ`c?WK zf)gN#5OTw17>%Pi%3uh_NHr9Jst6UXfoYgR)Fc9HU{a-#A~35$5sFr;Wdwn0SQMpY zxC~q~v>MYeG(zAgNug?(qA&`RX-EyBK|vcR&ZR1X+(sN$A(#|rWoib2F$BdK8A`G; zTt(0*O3>hpRG|dHs#%Jq7=~1%FoBb56%4~D$}%XIHw3vLFphu|)({OcjARrkr5F}L z2%1D;29e4rihxxr97Qo4rq!TI5QGDl$`lP;Cn0d)VCcM<3Aw3Bl2OZK2(3XVmQ=9} ziIbERC#5oykP-}vU^GF)Fe_70AQTi%v4jeiYGfz{Vng68gR4je!=;E+gM-*%D6G-2 zFiOJ&jUXs21rRgAxV zdynnCG7icepYq+eA5VO=C8=uI*xm~N0;e@f4~1Wqdfmh+qaEz-o9xE-Igz=_xU+C? xZH4^fz^%j9o*Hw!M~}RP^ltdi9UqlG~*%70}ta}Y)HG0mKbr4#KP-G@-BNNw`0B?Vh5kDmHD$3YVgZ&hsn zxhVKfxv_Bf)c)6Rn-?jE7q6Z5zDG@V*Xx(=M#CBI~rCflLR_NMnLyC=SBIjmi6 zT-LFQ<>m;_4N&EP)ab)Anl_rZ{1!N(__jl)tZPgB-iV7UdbL#ERW5z;WOVoFT^n}C zmtCk`=zQXz19m@kbGtW%h|Rq2TkpC$$FCr%=4n;+?x@FZqs&KMa;G5E{`L-G1tdAZ!H`Qr#A;}x_v0~uI=cO%QeS+oE9+0uNHNmdii3f zk)0RWAn!N^^i(vQ8&-d4)uTo`s>n7{4mb6iIyd!6%!zw`?n{pjjqk!;cXzGq_-Idm z*K_A&iFFMVjaZ+z`&ai5*Uo8*tJ&rL_nw%X zU85c>^hDim=NXREsw|rm-~DiUL3+3J^-ro_&g%n5&U6Z^Ughr867a<49&)kV99puW zv~Q^M=E;Bcc<_B-cC6QR_o*Ap=H0E>3$2 zO;P9f_E39%wU#+Fw0Qif1+^q{Q0Aq~Txz;FVresyJKgc!!`~+7G$-`Fstw!MePf5K zBg~nud5#6u{qj<~oI1d1yfR%kOh0nGY2-Nnl-Z6-1M}ISIlVfRtLF^)LQ_;VZW1+r z<*yZ*Z4<(Oh+I=Px7UuYLHPICV@ko&Ze5A3i(J?Qul8h+F(rwEP_U{;8yGV*8A8M@ zf{5D*A%zWtP~t@)Tww7-(23uKD8I$Qh2J~iBJNBS6I+TM5U~l;+CdBri!o7gJhXKn zF$&TerL{?G6fq{)f&4(27!wmaU}6WXwTUq0YjL`&nuVO zWYlR`4#LE_@E^T_YRklTkuc%noUO&gGfSDoAHYgX!dj9tiL1d{OngS9Os)4S>#Qe) z&xn*s+$Yw`6Q2<&Q|k@fN_pZlB4rX4#9DdcGa_ZOsy+#~2Pu;%bk@ofpAjjOsI=B% zdaI5TR@HIjtvXIvRmX)xgygp)tg7R}iAKs~RUH?OS5hXc>bP*$lQLOV$AyEZl*y_( zj=ojL39IV3aO{@m$*MXoJOfCXtg7SaTXmeYs*a;?)p63QIxakwNq#%hsyZ$_B1)O8 zs^h}*s+7s9IxajsOPQ>y@QKEVip<4B>4JjCRAqLR_7V z$1XLkn^IBu<3}qZZz_FmJU<*8RUX{4S9FVEiWIA@htY0WEvGIe2mJX*@coeYd)nC@ zFnIUw?t*@>DXo7_n8PVHTqeb8Yh$z<)`FYg-p#@fzOTR2^dSG}`9`CS`r~`=hS#hZ zc4kMX83WU#SZ$q*cEj2-nY)yj;(7m4w+_3HjaoJ!;`1--9A3V6SGId6?z*QkUy9XM zJlhScHvQSv#eJd&_s~H@=G6u`g@67|Cl5A0JZ|0njKgfN52aZA9i`oOV)E&#h|d@H z5A)iV-NQf0qq*L5=k}BV34@+3zH5k(tvMmZYHMkSh3daW~lSobsg_S z%sCm;ebh;3m-mX4?>al(kYcqx4YwOs-$`Ru7B@_D8U9SxyHi5%W{;R2o5P3RVwDe9 zK62Ur86?GOdmwH%tbB(Zn!3cH#~VW?vMayr_Y+rsD=Ow$#iPR!54ocb%hpJ+1h=Q% z*VdHa<@QV6_Uy=dyym-TpDV+TO)NC;z4m3+?bzvaXTX2%kz!dyaI$^ z=(MWBiBs<^bo=D+Y3d@eFSQ!t^%C$hkosp`)JG#ukBA#cHWxZ4PMvv+O577{s+pANwF-V@?57! z#Df9cg_gRah7yOXF0%(In_6Or8y1C*b??}~Y?fkKMCGr({OxH`SaX%bAFL1i>}2rw zEn&q2OA|bh3CAXde=WEo;f>u zuFp^^N{VF>mA8%bjy+fVccb^|{KyqCwj}|R2fT3QRLg(wml%1cb8japmPJ(FJ2Szn z*YxkUKkwjgGxBV7y~CjU@~o1EQGI>p`~G>U;H(tOA}a4%G5y7{sqRd!=X*Dm1B@?f zKmF76$npA~&3CWN+g=iKRElL0l|Px;ZJzVA?60GkWf#pCW?ijwUO+-?{YO_=aBu%`dp!7n;-3GNlKnYu5$#QVLv8AQ*%4rHY`oQI6|%Ivq*D7-+1+ zaZ;xxQH-MGB%-4zJxyXbtWz*Jsl{jn(`!+jBp5=+P^4Z@VhA5b4Gl%)I09a&Bxdh=!+lObhxdV3dYw1WXTf)8h(E3sQ@L zY-=fyBm!qi7}Zlc2G)`Y2s0kS+Z0Cu6YD)Gvp}7fjx(AdP~gQco(Z7f3x*Kz4@$kp8jYaP3ycbiO*MuYEJOjv zZ=@2M?2p12j4KcVM{$rv1&W{)Mj!-+q7;gd3OwNdw*c{h!Dz}z=0IhcFvc4s7saRqa79yGQSH(*FGG<8MO22YvL4bi3dU z#6lWihz!9Szde=U?F%aky?`_&Qq5hUyRHfQdZ{c>@E+xJ0~6pCby|}*`5LObZm7yi zWeo(Ia!=$u7khezS$upV${Re_z)9+dmsu!9>OtfF^Y2C0o7m;8n=T{hO^zlScLU=Fy8r>8zi`cb(}3l zUZDGKrbDgWa#h2ey-FkumD?Idx+8K#X`6mZ_*EKb7o{}EHS2gZuh PRyKr;l6L#xSw{W]' | egrep -v 'Date' | wc -l)) +lines=$(expr + $(diff $1 $2 | egrep '^[<>]' | egrep -v '(Date|InstanceID)' | wc -l)) if [ "$lines" = "0" ]; then echo okay else diff --git a/qpdf/qtest/qpdf/enc-XI-R6,V5,O=master.pdf b/qpdf/qtest/qpdf/enc-XI-R6,V5,O=master.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a3774a43ee2958c4a9fb0119f3ccfb4daee8663e GIT binary patch literal 10933 zcmb`NWmFtZ*2jTBa0^aw3C_&m1PSgI+z~Jr{-13s_ zKD*BccF%Io+aIQTs=KOx_tw9v&aJ+*N@5Z$AXaX4+U1quadZ@34hkTJy|EQKKR-ai z(gp%{1W4EzLBL{Q6MIweeUYORgaQNt$bs$5Ar=%MHXeWi1sg!s-2n_xG`3QK*a`|d zK^(zGw&*C6>T&dN7PLn(t_U<*=Hg7>^%D%wtQTLCl|r;5s`x{-l)2hV#Uu&!)$VCI?4nH6`B%{;-Fj ztT0~TE`g1Re&&XURiDI+B@=9Oze)wb;%m@r2TP6=^18cS6ewdy8kck*NQbxyTn4!m znzft>e)N9kBS}QKEm;s5FkU`~ry3h1KL>LzY)(u}`@xxBq)OcKn7+B*A{qbtL)LQr z57zSh%UsTXnEM}klb7q+!0wjlk>UGUHvpeR!_?ddTQeefShD0Tf$%r#bv{>=QxV&C zRtF*z4#LfHjl)UNO;=yC_C@#7lN%yscrn(jp;1-M02%DbvM9WsxZ<%NFwYYart68@ zF@w)n`2DuN)Z$F{MdiIubP{zhuQ$k%dx!eXKj2A717?nR50yO%e}#sNRv2St@Kd8V z%q$IZ^)W{>u;B{M%RXvsW7++b2H;_l>}N3T^|knMC7R)hjfugPVv(I!67$*==~YFd<~71L!3 z&GCnssTpGOFaLyT{dV;SDO4|Qxnw$*w>j4|)Uh&_w4!l=M@|>gSv#Wpc68P@VgF~a zU~nME#%Wh5m1dK~8SP5Bi=+A zY@ayO4J+Vzn3A^+j3g??zsx6;+~G!)lbJeF%`~cMGZ4CVpDCQ-6;@1aUhNIDy6Klo zvy&!0{E9kou5j6MO2?LgqERPkH9C>76Ol$Zp%+stXq{baZ>fkL;B5Hs7Q^IR?S`4A87ucEy&Ntugh*>_^5A95$r$=cqHkz5?*%RXC>i@Jn+2%hJE zfM;Xl`fIXa=lVSrvI8HUEe1_mp9jc+o{E&mIL4~uDSn?za;ZaZRzS3t9R*I%+KEvP zT)s^hd8)iPeP|frE$0dzJtpXLN#uub6+D-VwtI@9)F^HO3+pO3S5FzCKDc$Bl{CzJ z%_AzEJFDf!5EfXCeF2UAMLRmoS+jhgC*QjzvnM~7^8h-opWR&YeP~*9QTTap)QLLlnoK~3>jc8-owIOz?|wEM58LTA__rfRia5rYxs-q?Ee=PVSYBbfDK zORr3i&;Ww6S_3f_80w(8T_yhy}I=K;=cVrhqC?G{`}{H#smEK`gJazwN4&o{eqe4ncAXV zBpUN{^k;_N?#}rD+!DilM5*!^iQek*DF6hMt-FZ&#@zG?M5m#Bb^Q4DTPV!u0-7&X{rA=O0Vd|goTKK8VREIt>G3gDIuvYq@l8Q^msC4yK8qxC zxnjxuhAUmCo#%~ASGRYjU=bgfvpcP{r9o>023c7{06}%n39pC zi_m?XhPEbj;m_vsi(&cfhpD$OVdW9WU~h^TTOkLt*Zke4qk4xvKU7cZx|gpg@5x}) zYEP7Ajwu2>{Ky&oV6`}QZy#U+0Us-qm9aW+L5fqIz%<(JnQV72`}a z_aUf9s~o~^w9$QS&}aOr(0=%98oE+9W1Q9g9CGGQZ?xFbG-zip!xN#XEe5Mxz2Te_ zSb{y^`Fm_~Z}ASNYyRjk6I}Z=48?CJvzq!5u3rWRk8K(+a%lN?|ExWWl>79krIS zYwlm55v{8vB0#62o0V!%*~OdlR_Ks~u%Z?4r!TS#M~q7t3;U(#Hk#awG2>kOJ_Pk> zp|^=VyTxuRTC?BOS3_U-HWP_99lq5r`Gg_uB{2rh&xfZXMvX!yGUD4n#+qSMxzU5y4Xsn^nh3YL+b0C zs>Fl4-51zP2cvfj4?#Uz=&xQAx}5$jSZ>#?VcKYLYAADv{N7+bwigHXzn4XM+<$5 zagC`(M3SX_PqRSRJcQKE0cbD}Q0k*L8b{fs9w5R>Eh|JqS5=^ME+Oj*A0I(P#Ys69 z-c<&0Q+q|3=`bGEQBf-}p}fPSEz^hAkF`An^=P3tHT7Fa#atp73??q$uM~ZJO@2sx!ti<;O0*=8B)k*s9pBHZ-<{?2)#pW%7j_nMz0rIl@LR(dDMG>!gziV zm0tI`#1CM)N2$zScO}>Zc5z6E*i!Y)Lr{+vx|b|)wXkfTeV5?ZXED>G+EFX=_bqG7J7!6>X*hwQ$!!m)JgswdQTjmcRtfe%RU!M+njMcYpEiZh5R&PEnVs> z${+Hq*JnQS9gk3ll2>)~9E%yX&{b+SuI>apo4*8?`-UKHw*w!7dbH5nnT&)|{0I87 zwgq?1q?;n$DrCoOskdAjL4^A*0$7G!YR@9JDAeuUJ91wHrehfF>e}dm=S}!!AvEIx<<4L*}z;Fu>+1 z!2?hnj~04AEIOT2M_iYrZ#?c!rV738x8q-3mW<8I_z|a0Y3A;v7Ie269s1h|l67&o zavjs}Rs=hm@8-Z~iK!_wH}Dod1};%ftZ2X)*j(!x(Lu?FpdKxBVHRC7;VUJJoKx?4 zSA-o;_9U{mFDF|zM$k%Qd}8S{Fl_ohq+&#@cIo1kAm{A_^v4U+28tQbe-(QXih_0u zt3|G#c;IPl+~s*wr{3Da`4H5jg|4;7aY0^;psU3+xvd{vHzYqvNH`DwwLUbm3u6L5 z)Q!UUWIs6BDJ4PfDTvsH@5dmX)oq14C0eJSJv|YZY z3k1;)pp}uK7i6=1Hv=|ItY(+;qurcZS+EEf%8q*o>d`{)ju;Oh$?m~(54ZwI8vcsO z)#gm^m;c#VI^(W+JM75T)!yv&nmZ3ft;uNf;;N*%FgCi|is{8JmjNI~n+y(hRJza6 z;iS4&n@@3GX;QN90VvK#3;h^KF!}N1*Fqw^BD>cloxzA}bFK1^j8b|{;T&1=Z!#>w zdKfKkRTy2IG(y3BL{RhoNgLNFZ-Ms*!MIPAaXTbhbp)eGg~W^bn5|UWh+jSg^=P4+ zWohL6^h>x#w(ZLX9(f5q*VNow-pEoR*iA3y-^v^^exZKOCp0z~cvCU`Wdf>x5&Vwp zt#dUs4t6r8f>6H!I;@x~6zb|3S5~6jGLjVd5Y(fE?to#`8yLf$XQVk*g}#MzHh-~D znxCjcV?E~m-KV_wOe|VcP{v5X=RJwOC6;rdkGYYV0T0rJYEbN$)a!2*J8BZZt9Noi zEhk=li6PJ9haQ4@w9vKf-!jKdP=~Vt&|ml9YKC_57wO(2u8NN?MQ>0~)R1^b80``0 zxvrCK=zhJT7n4U~i-MT;qwm>ZwIuST^gE=rbX)xld`{aPWbZK z29UvO;f|Y}OiTHdI+uO1-$sRWjeI{T2%$4)pZ%s-t%_pkkHyJ?U^i?#F-~kEcCc z#-wP{_{VU8i^mAOF$m9-EWU>3{>oKiSb-A@M>vd>Z;<#XyP%;E zc`LdF2@>^7H4<&j(;a|6wojr$)Ehd2lqA&FQguz)hMX0$_%Mq%91Uza^E7xFO=O%C zPZ0dXojMj)mWE8h2G{Uo8;XzWG6Gxr8Yo{P%|bW5K0L!&KlJ{hu z1b;$j8RM4~AF6O-y5L$s6W{Ah;}Hnn;t=^ZuEA=+-a&Sf`R)-a-Lbc#fj*bd-p~z_N6}wqvW9S!63)NsC`U zVwbN0ekIi8+?8L68~VH;%h4a|+-3H&vxS9bDdwbRUn(GuL5Rkpi;(^_GP6GA2WmV# z%5dC#qlOlB*9Dy7^Bq5od`mP{XWVLcbQ;73p-G%|zRK;Qs>z&S1(}lg=jnp}ly#8A z{qtC(&&hlB3Z`ogHaL~tItiV`h3#SX_O{_x4h*}!>KDr#Qo*Wpide6`!4kFD0v7qt z%^3$h^`FkXHGw|8m^KUewZgH^Vr9c#hcW&x-pN7TI82ME7aFb%QVKbJ)$)zKrvzz& zWwD+A9o7$hgdn+E-Q4&1jvbNoiJ!}BbV9$FP94py_h+2?2KbV7X>P#()J~{1rbg9k z#Ha417}hnbFuZi#9NwHY4a9UFtRNcz>rZ4T9Dawq{{+2=^EchOi-hyS*oi6!icpbu zUo~V;A(TSLFG@(o@I0b5zDrnkUxPX8Z>f0{ov0ya9qZ{{?^o6RR+`kac4KjoB$u*Z2?QHkT#lyL0TouD#DnjNk4K4BIDk&-f_7-zE*p zuoXo#pF8?QrrNHo_)Ug&4P)__)s|sIQdA0wdB?Fb?sSOfP`r)h8C2G{B$erh8IE0Y zlF5^(ia1lO+6bp+i%0|;pH9bsUsKi_E=1i(CP%tjW+wqskx#6HoKv#;fc&UHu0j(Y z11stqm0ZbXAfw&O>ri{!Y{UZ+$)4b)xlZZr3lzOOOr1UkzI=hz719m&?G|!B2VYV( zH~nj7zexv;i40r;8XT2ZmjG6wl(nceBo@vP#}!<~ha2jMOZ zer>sL$Ye)jG2vp%#~Dp!wa_dR_)dfQIosf^IbvPs!_?I2VEQgv{nVgt#Ja#rR&JTujpS+#CR9CW6rY_&qDPgqO6v|fDa(6; z71@1`d_9sRc_nHostLyrKlyaMXNkWUmMcO9txmg2)8(`OmYhz!kb?_?fD^aFsbK+0uL0P3>= zyomhO7iu?^beEVDc2D?_yXX%x77`T@LNuWXCXR&Z$&=R5{+XE-8@ncWgaW zo@tw1>P8NZHKCdf)9#_g2pqa<^e^7MOaaBS`wRt|%3*y-@I>Lc!Gveh3MRK1XjcD2^wM(!O+6%ut+< z&m4x56p%j@V!3MFk%7ZQ(;=MB7xsGhygu+SStYw+DAnnd%f|zc!^Ke zU6U8qQl0T<-mv@LqSc_;5ukKW2`MSf@0VY1y(c{<*{@nCQhE}!J!}`StX3p2wv)=O zIrk}`qgoiDZ9HLbf{%-wu2wM3rnjySZV&mZ?AsAc<7+ET5jejrN}M~KiXF3@?j`io zvvMB$zJmAZyHq>#cIUW9+)kJ;?akjk*ZTGnnYx2`_j7dVGs<5}Y7r@MfnEX|1;Wyq zPs#TZ@{YDTn)D|Jh=_Z$-uD&7gzUS7)rPBq&!Nbr45^{%CMc$STQ_)RN?8gQ(?=VS zrnM&|!XIsN(=}(vUgZ)TwQY9^6L+QX?1!Sjr&HMCh;#N_a$)u^Ae8^T%V7` z(svmveM*e`3AMS*bgMSqPnHq7g6$OkV7(Ld`R$K7=^*BeMXstl2KN_Vmh>C2>*8w7 zBIU}!;hRkj%36IBOPr$!Nx9UV7eUc$9PB9>S$IyDa%6TgI8Clst)c$@TZvNi2KZ~x z^=kCjcovR_9D$=S9dBEdTuYKNUx9WDHvvlh1asvvdW1Tzg?_)5X_JC^3_vQq6#C4WmL8j1sRqVsNqok^nvbXB zSqS1-&Zl0KwJAJZ6Y%*fyE(0tAI9;jWhO9CMiX6@AZa`fR|;^~VPp^Zz}kBuww8q! zmUs?vJAnt4k%E83=d0O)xtxIIKr^-5av58r9o1>|YvEC?=|N)8P5mOPYIkW%8Nr#6 zCR^*lGM9d6amrW46@oo4H$NvCpSzwJ8JbeR=G>&bn8RNg9oB)8Jz~TQkQ4%eFnxKa zyfB_$CvA+O)HPc8M6rvy$r+WKWP?3W%=;F4_yVmSl556$7uZ0S$vP%&6R6gWQCeY9bF6aoXrXc z8a5kAzdC(Es7^IU3A>_N`Qgjr>=utV3|#v*P5suV1;_^)<=faQ%4S=Ii@Fi-`4?ZE zUg8sjztM-UqMaf1KQCpNH20q@`ZdH;z1O{o41<`$?C(^RRINmnFKC1`Z;&?9*o5q| zU`cQ6pCA;WwMvg_PMYyRG{E(@xJgvvK9xrWV*2MS>K*^mnIVk2zDG8pDe_Pu&ILDR*g!%tH7yydm^z78F7+MYZ6T`Py%iy6t zIS%vN4b<22?ecTb-jnZW>d0QO{&8f9!dO4~~kJbl$-8-4;oTSY>WpF#NYFN3b zIAiVckwD%cnC~bGg8%<(A`AmSO`P7IFTMPALk=xR*I;~rhfmu1XIb*OlBrf*dMvPJ ztZ*|d13;aEQ&157pXp6pe@nO!cY}cKoGk6_obLNySWHCx{v=}L1Xc$-{w@XFCw&3_ z^^6?Y1#CmX^QY(kiV!I{f5(){07YkrjinvfiGurg@{}?_$;ccmXJibv`Q5OBv#pa3 z@cu%He#9^-ccra0I0yAz>*>)IpY+BjMqPS#5k>IIr7!BU(KH4@LALuRRRE6nzNP>P z3UdFchxYbEoeTz5wwrTyNG6?9tPXDmQyKLW9|6{W1dRBIgZpZ{|E|U*C+yYE&_CWO_WPap(gtzQ|22U-y$0>5AGp7kTTmZLbZYEa!qMgP8{;-*z4T zks+vLbU)Vay&!6D<7{i^M8N@2as-=Nnn3Iw?~?`Z?H9K*addZpQ2d#ql>eG${+?qf zEvCcIEy2dg!Nw*k4ipg+=j8y2h;XoTa7&1BbBMEv069Q{{OrOKKyEG|4<`@T{cR3W zVJ;C)P7z))F)lH2k^6E%J%F^GnLP#9?|m)hzosvCPHup*z5V^`e~%PpfQqFDm;wa+ zXVQ+QGT02Dp#!2|qX6Ca)n9px7V|bE!%XdONV<>CuTR01tnE_t z`cm|Mm+t9R%Oahn<*j9iS|yL9k;eFGl59J5GZoy_^vY+KBHldWJa2eoJP-4o1TqDI zs7)9gtNasVd#1p!H>k>$r?9MOYG0l%sPTQr>Yjdn!D*}_AFC?s@@||lz(CMSwLHLT z7WR#Eb3l4TD%N=`>cyAg9Aa;`2%@J&djsgO*1KH3~Xlcb2CD2j+4<>M)egFUf literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf b/qpdf/qtest/qpdf/enc-XI-R6,V5,U=attachment,encrypted-attachments.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7c1a02fa8d6126908ade1ee7dd04d63e4dc8c4aa GIT binary patch literal 17868 zcmeHPc{r5a`$w`x)<~NPS*r2OJhO|j&SWQRLYd8$SsFtUMIoh#N{dpdND>K=Hi=Zc z39XhAMM)HGM89XIl;8KA*YA3N-tTq!F2nxiEp%D8QM`hP1*63b;T}!3bEubpwY1 ztaN7h0UlmR7zjf`c!cn{3<^_DCZLT+^In+HMl_;DriolglNgYbfuQ_Z{U@j)JPMZ=&Sm(?$spup7A+z* z;4Ad=DVoEOc(hQ&phtxOep-bDG0>w$LJ!ylGMw$}U>m+tHP<<|A8Xc2Ey zBw0dLB1r<)C1zdHi9GFX+G$M zLY#IKZ7|&ro)QV1DW3YGtiR{Q>umB6PJzz$1aYv2Wh=M$c?@MIzk4j@+ zWzXPI=oB6W=0^z*VT?iL!lSV06fS+NHi*Fjj(SD$+;@9}Iz9kV1ya#1m06?*$a2nTMtcyq~C2}Sp{l>OS? zC_gAkD^)tV{oI9haAjvYVXr>m1cM_HARcgHKs`%O?l-5FuxUtP-ci10y=Qg z(t((=i}PMpov6?jsU*m_7L*|ZXpaa9*;r`U_yQnBDfv` z!{G(i35d)B>KVao|8XeuO&{xlY!(DFF9e4FQNx1E=9sg?z4&Nhksu650DS;^h{`E{ zx(abVRCBB;XGRD+luKjqaU?@R`9DY)fj=5K^VQ|DX)X*Nj3D5D=z=qw2XQ|P8h-d> z%eQ8ng1Fxb3K=QLf!q)t4E2i^a40l1l3{*Ga-;@9-C{ib6}jEzK1HbXKAbVyo<@>U z-mz}rnD%pZ=NmsqDLHDs!rnN#Jx=G@AyiM9Gjo;EQYhQ*tBOs(EQwoOR_E@?|0gM*+X7c(W96k z*HvA)Zf7qo(IA_efQW5$mCSoJ4-QMaU)?0{ZdoB_E7N&A%jbYC z;`D*mq|9lG{`Ysyx@SdR*b=a7XOTE#R4IX8IK8YubrD&TZXP$w6Qhhsa#mMrQc$cU zugFCoscNvZ3EHnebm~xh_!p<78FmE^ol2(lVq5(rXByVec$(Ec^W_eAg5<9K^pW0Y z2?szUw_=|ypTpIY`Vk9my$m1Jz%+ikwEuIx&48l)oigS0kBdB=8OrOM+%0JfPiBxT z#o;s5&`#%f=fs#ly6J!;#_Q}jQkAmrZJ%z{=}x?A!U5ki;$F_o6S>FssT3OosD>>o zUorj6@eA{gER1;PY%t@-g@(Kjxwo!TANgFd4#GZ3C6eLo`MrMx9TgvTHqqMQ zaH{^@@;j&?rLcP>ZJJKnJ9(XI^Yd@6ePxxp+&jWFM;X1F$IiG?t`pn0rfmP7oMpE* zt^Tg)E0U`<=ZmoMmEVzn_U@mt0U9EpvG~Q@6$&)?T@ysZT>YS99Ms7{1k9BV9qT#q zdnFr&(1*DO$&g%Sgvdw!7H=^|9zTe7N^l2Xl}TEUoWS~V(e*Fue}uGC!g{_cle8Y3 z!1{4f=`ZVljH6S+`f=mh|JU1Au>k`mhrLAdLHvQ;7Tox=hjoa1#L_jQ)^Qi2nq-OcDV6 zL;wh5K;#snK!Glk1b{FR0K!NR3ZAA62MBce4*+m~Gx$SJ6#aq1SP+~-EGW=rvItBR z0fNG45DKQJjt>Y40{^xTOcVuz!gvszLOdwYWwJgnQ6vZoBSLTr5urer$s#aOGzbb~ zLevyuLV+%mMPQ>g_uyF z%VZIlC?rG)V?xvvVnTr~lSN>nm=Gn53DHxC2?e@L5&>|cm=GgZj0rJQ zhzSL{OcsHOVnU2CCd5o3CKTv0Sp+7E2{FQ$5Hp3CP@v0X5tt|@#0XmvklC?NXYs#=q6DxF@$c-Dp$!+Pj%|(qD4FEZV;2mHo!N z-sOEJk+DG@X&uboSszaa91iQjef?}UVw5|O1kBG3xcjZ!ZQ!D4Y?abY>9;AHumg0s z=aS$->fqkp*B{A?bcK%AslE2~2;HA_Saf7GE} ziRx|pqv^&#zGv}mkz{6FLUpX7Y@uhe>VhZl@l-X(JAavn}+{==@07aLBi-pB^=$3jllopNNy10gdmDx1sCui0c5nO`d3 zJ|A|^{Cn_vGaG#bnz%9;yD|;rQ40M-gZXx?Q&@|O)g4r zE+Sd)?lSjv)Z!{?Uy?Ze#7-jjBi!mJ*(3SkU0u(+h}Khi@|oY&d__XtYLG(K;j#RI z^`{eC+VK-P$E9L=LFqUAyeB9b2}-Ub5?P_a!9G6|B2^ehcQ1QJAoAOVR3 zv1lY5fzd@EboBu!JxNFb1VSGWBwC`N%qGAkl*;2L0Pz^%JS~vS38nY0)sW8oeF9iPh%ViqD;3Ij5lks9@$$B}C0 zjBfR)a;N@OVX%!Gq9NEoEclNA2gwlz-M#ik%wRE9)SIQAe&=`sf-^C8*#84N)EIVr zwa1|s$b|BzAo#-J92S)5HJ0n4wRAaAYYoDb^De_$m+CDg%D8`T>HIULFqd!3#jiEZ zop;@J%W;*6@H#&$x$M3+^}IPUV&$8BfW2*H-D|4WKU{Q|AZC(sSzU6lV`SlzV~f34 zVA^dW=GLd}Y(W*KIKAHQ_xja?ZI|YD&qN=q)7WgV$`W zozxlzLpH~(^)1>XLHgADQG-=Dc;xh#H^bDc8Cgwrxt*k4{j}H3`D-GM)a<*WKH#4) zG}fm z=osT&ovvjxSdaf0l(VsXi><_6e>>5qN*euMvFxi+yct7 zqn}gLx161C-tg^&j5UC8`6TspI(NXlp|$5g#z38-&Y>MQ%*As9yUo0`qSlI~?(RSF z(By2IoRnE4rK>D&p*!y43FEgbFYb+1D>S}cllbIpx#rn)FPp1#o>;cLTb4aYK2UbB zSJqo3h1FY|w`BV5rnSS!(&8a~glerC<3yZpQqPK9(cG%QJS9 z9v?T|hAc;TRq)seW&Ejgxle})d%K_Q{?WTnp9eI{>#grjZ7VEA;H@QVn&p#3D|vB; zo?EW#B$^CH9=+nxu)qBtmHJ0%M0P)Cen|74#nU?C8@%?v`&M+_CxP0KH}9L`qeC2; z&zFy4I;H-K8mzD_4KR?@+VVIChhN*cUiI0{J2I(dBg^_$p161Y5Jv1xM3U_C0Ie!n z^LV-Dpps0fX%A{t+n$Q6)!nx7)-H57zgW_L-<(*rsQ!ZI9|Pnreqt&(r_StHU7=B; zeKV=?dS0QkVeU=z^=lO)L8rW{id+1ThrB-g?Y*%vJ$GJK_O@rQwpMUDKRgw6)!RpZ zaWqG#yRG+35jpQoU5fqt>_aJGnrAviS;uI#UL~%T#{yy_yg#08FU$+Gmrh5uiT5LZW94=mIq#)Uk%+FL z%H-Ldd*)b*870nfd}O-vq?ztI@{G>eHHV^o#FtN>w_@!^$DInuvtnm&$F|I0N{?`5 z)xnyhAMYGmtVS#ODstA&i`&NAEk2}$3pJ5eol$-;xH*LM_=#)mjmEb*0e_Ir!E+0@ z>sjTt$fSU*$GY^YK^E0|B>gB+@tJK-dc3tHGLzG`c4w)o5&4z4q%CaSwQlfIh}}Ao z500fOF}=j)b5qG3%pt>!bkl||b%W%9@}9Kln7-}eGav8pn6@zmU!ad(+`mXZgrQ2# zN{UKgm7hzVG23<43%sM6MnliCyF*GZImt#+TYEQ9WM>wI$s`uKG~^6(=YLF0lr>st zw{i1(htcZl17+P3f3Vy(@;1Jg)R*$>9oba%JYnw9t8w;UkG)=f<}fhl>KWXA>*0n$ z=R0X1&MWDCYU@!Ljytt#^=<^cZ23r-yk7S6#hQj0pUo|+Rmu(}WKt~M1_!9JTH!Fo z8!0<|X&*x0UTr;~DoO10lumch#;4C=ZhfKq#bhq$Dwr{VlD7ri?jQYdK&!XAw=VAE z!_Z^WoN4lCk$Y!1*|e?5@m|o#Dyr*pTDM~)p??n@p;&#p6*1hfT$jVWm{YL8SJd2(6YjMwULwjq+*+!q#|o#vo4rm7aMD*f6MM(QJNC3? z;|Znr(GlE^bL%SV?#4eGvNAY&>xpjh@^#);&mZh{~{5Hht^AT6TooqG}TtyF{aZV^LK5 z=U%I?kG6d&PJew-H$yf0?5zYnBT_cp)8V*E_Ub@K#o`+h1O5I_#q8CpzMHADrDV3K z@R)TghV6!0^)9@2M-IF!tcqKaqqf^YB86vm<>jYsndoh*Z!xW|M^DbLTdTJ|{yb)* z*YM-Y>hccH4P;5&%ekoja|?QB<&#p_p%M45?Xz|`>~mIYG{+viw_!oea`y|Ps22@~ zqAO;-E;7xNcFW2z9sQcVa2fm@$1rEaqMooAE&V)gPgzVt?jdgK?We`(pG0kG-M+@j z`*ufh`sKmg%?7E~eVpscr5Ug}Qur@8aps-3T)2#Zv8%iGviDsdiZ_e7whfeQ+}Yu_ zU-ayWes5b7zrCN!nhFCBK1mp`E9+iyQthpFU~^v8QP*o!*OV?B$0D+5+LC(LPZp20 zTi9DXeN}G{{!z9RdnVKI;=Z{jHq1S^#(i2N;~`VicjNn6@%uAOBuln`;>~~kASV0i zInMxT!jb10`(p=5($d6u_{^@ z*lQ8#+nQIWEeJ-GsxK^RD$pR=XCEoJRDLhf-zM{BA}&8I;)!XV{OsNewdC(spER-8 zuV*xpf(=fDt14!C*uKZS=&PFdTumt(b@Efij?o!jpzJ}(g}~*4iu9SHLo>U?&W#Y8 z%+$qWxkBUSLrYJN@bN6*wLD2ekuytz9G<1H4`_@Qmj_IvUbyl z>|GlVd^rR}dOIvSQyZxhqjyG2B!_ihz|Z|!t4UWTo;sV;W7 z9JO`v%w^8P!e{BKY62eOQ`A{+28wF5S=2*^>OTIq1w-Gl0kA7y`ih z_3I7RqYT(we*j4!5C8-SfFK+y0T0>0;!(oktPrgqLdN7+GD2wFKn{QE1mnx4P}!k8 zV@*wdL%%*h?P79%HN*-r5ZGuyV>5wp3J2Qzf&he7qSJoW;e>L71**|$03(?3V{-`7 z04cN{1gX9Kuc|_uV={qXwm_;HK~)T)30|Pz2L2q6$j1>5eL;dN5@(1)8KUseT*eTA zFarLg=O3Bftp+ zKR>u1-VaNFzElK_FeZ0g-Jf!e0KT)0bG{#*Ew~D~Hq@(tU)@S@>z6P7w+?REpeg$YK1GZ-qE%aFLn>C8t1v#o|?aOnptkh|n zN4>Q+%evUL?RSil3b7l0)ssFw?XL3UnxtUA6gWe7rN@U_uGi>_4I_YVqSxJ^ARpsl zr*bxJPI}riIfik47B>9eHl5CS)gANpFTE7`2iyR2xQ)81qh`&f#I;c`Y>vKt$QZgP ze`8ofR6^ufeUzjyT*qZ^|0S-NF<5a3{=(s3p<0M=MmQCmzrWtV#(wGT+Iu0^ORXPp z&VwKV4Whw9G#YgtjjqQa+;9k|M@^sW9L+p@S;4`6V2Vh_#{#`$VX4 z?YMpTPER|1*o}EsoaSlL3mkR_izlp6ZLI}NA>Agz}y@uYq6cI(5bfid^E+8UZ1VII)OH)KZ zP>>>^;v3?xIM1DX=3(ZUdC4D~oWt2Y-~H_F_1(>!i$hyMNdP1$LdNm#{i|^@Vwf;M z2;gDkOeP@#RCjVmSR6q7%Z>u8!cZ7k6eKDn z43n||s=C`D0?bexz>w1udk+8%ihig%!rj5g5daf973YU;H3+Ed;ek>mg1$iyg$e+N z1N8$v5kO;Iggwy63QuGF#~|$5a3_!0)gSE&IQ3lfO-HBMD$;`aJ6f5LDizjB;q9e3w}FNyq|>!syNLZZXmpfHX&9P~9tMB4OI`^zaP)la&_`KcA4f$$gu4$=!NXR| z6X6b&SJD8=D(V^j(@qF#JfTL^UpVx9tljObk#>Jm01Lx_THKPG8bzN5Uxvd;Lw{1JqLFo_io=VaK0+6K_PBFXbCJ#^>AOH~(fdRD;2NHiAc!om@OEKXzXKzilBF570o78&}(r>V!k#efyx|NoCGs`tNEP8O)T1 z4Y--Z;*KekBRoJufbhS;1U=9G>UPBdAB4NNlZX3h+>lj}Q$!u)ti2J22;}KiAS$K- z|G7sU;fHVqz|kGAf$*`mv-YtD2#ca;t(LEkD=N@=14K?kpDs|_+5w?%ZG&(>P9^v5(OFn zl9CcZ7ZXk>%v|Zm6(O;g{3sRJ_RT~;E%9x2xax=&lE2SgV<4Y%Mw>IK1s zgi*l&3_;(5ZkwiiGU!cZwGRER^9(?CUYpn*5yZ>s2v-T)W`%@!RQ|rHk

%IMs^uu+>BO07THS9d$w1!v|$10F@Q1+Dk@DZe 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 F{|CH0H#`6U literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf b/qpdf/qtest/qpdf/enc-XI-R6,V5,U=view,attachments,cleartext-metadata.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5adff443da8833c9715ec0a2a2011981087b1758 GIT binary patch literal 22400 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

H*aiu8#%5OG&o_l8P=JHN2W* zO(XZ3W#%PMILPjzCH@Lz*(1p4NvH50Tx5}?G4v3}_{J`ZKMTCcH;-sG=)bYA>V{Ki zIz8r3xR!`~LQ#~0p|V>Xm6zTdlatK8m!fl9_lRkxi-^waE|IaZ6@^)Ou{e}^vEs|q z7!g!l&wt;zIdhTzzt&;@SGdx@@WXzt$wUOt{9)0wOG^5Z)3t-*2qxf9nRQ*9#{%#aWqAx^-H5Si{4 zYkXE;VHwgLfR}%0Yn>`ZeoSaQ@2#&q1=MpEAsE;g@O`#*G)RAm*a`*<{vEUR{{Y~B BY7GDY literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/enc-XI-attachments-base.pdf b/qpdf/qtest/qpdf/enc-XI-attachments-base.pdf new file mode 100644 index 0000000000000000000000000000000000000000..4356b7e78f0d5a26f8351a23734473634ef6cb73 GIT binary patch literal 20751 zcmeHvXH-+$)^-pA$D(3B=c;Y+aId$@e#yx#X6{5Tnm!OqSqb3SXXXFq$dxtEx(Dh>pdl4ccq zGt|+^%7jJ&AwXvfJ62g)u$HY8iQsDML$U-y!CF8VL>dVN>j2TxU^O5N3Ppmofp7@e zkn#fpA`MmtngS6>808ntF?o4bl9MI18Q@<&tV|fluRc^sL}yEqF2U8&jnbovvxB>% zlN%5L)^#OW+7ijmu0SYEULK6Ibs&>m!8iv3ne?p+SVPrRRvHIGB498THHfmR8U_JX zRz|=P(l}LVgc?j4f`H1)swt~U!&FspDk`cd6&wPMR)MIZ;3$L&Od5s6p^);&z#2}L zBrj8n<4_p&iZV0~w2G7ybgD_}su28fXc8+yBt zz{dI{E3lC%6bJ)CDFC>UT}cE-RwkQQ78aH}%*`##KWt;SRXk1DbI$xG9f!g`7z7HF zrHOz-vN>1z+*SZvc>%>$^aHF+6y&~F{*Iv=nczzHawS=@GC^3G#KhEeaI8!}ai!o% zwdVVnDFdNs#JX@%1_tuOB`6F@8AvD!4K@Hm;h2B1)|_gqd5?v;rMdY+bKU(wfSDo` zN57N)EIsh;7FDgFu#~~TAON$-8Cukv)z#I24?)Q^+&l95G-%;maEK_xg>hJU))WpY z$fL2nbSQ-*sGODd%+g8vbIW~!7P2fXnP9=!u}j;yPFk3M@=)lH^OWJGmRF^gr^GN6`RgmS9JSiFW~m)~FGme_ z*l+cqCoz)Yspz^v6!admc4cJ?x*)<;2MKxqtRmf$Iq zlkk_blvzxv`J*d+lAE);E0ILS5l3m1`VR`ktPKIxTvunJ0f`L6Pzgt$azfvkOqtw3 z3ZYPE6V;pF45W_D_b^z83UIcrZe$=F{v*7l;Rp&hA-}bxV_|1NcKkiW%MU+veGcK8 z=wV(NOj2Xwj669bO4D&}M_zk&JJ)lmHCB?l92r>CmpcSCKjw~(C4xbg=R0U(#(Ip6x$1({d`a6o$q4>z|_ZHIyw+0!0C|84cq=UXa#_q$&)2B&;zQcVC4>hRmr zIfnsz(gm2Cp?7qUchVz6jvo<%eGGE}$jF#q+TM-}j1@h}S#Eb%y%n-OLO$$qIuOS> zIW?$8*R;5-lO3VT!rf5Ql)Um3!5zFUCcS|8Vu!ZDuJTlPSD-P@K0(2lgU(G${9JRo z2Vw8MOBQ6))7w?k)hg$`G+|7GCia%OV*6(_f$kT^o7^UI4->5tiV{>=! z2j^8>WA5PXD~_Cgb^R{E>r<{-JjoP{$HqZ`KLhSJ@hUo)*=2gqe#RUY~s)ZX+7p z)fpasi8Oeco@o?@)U`={HrYfnzx(XC;PZr+FLQ0q3pQ_IAJRH+&RkUD$z2{V+)LN4 z>}-#cIEW-C<-k=t@T~+TuOq34J=_O+$McG%89~0a^K>>xLSvIWU~-3^?5#UlpBUfY zslTG@5Q#i7K9iN?8z3{+5p5LoWq00z;l^>7;rsQe{RW_f{02Htt;Xrei>KSlZB)_w z1*IOTnAY*0#+4`Pb03rQDFoS{nfc(=db^OmX_a}Lo7TcUQ2Mx|r^&L|v)Cm*J+g3! zhk|~F9_KaRJ;HqOGM4}dlq2%+)M)LXwyfu!?f98na6hm4_xwCH0Tah#_D!;kdCwdS zqyZF1@-M8%q11`|ee(Rcq)`ay#|28+ka8`dPE8mBXlO+_mZnT{7zAi&NjcuHOP!U@ zK!`Zd(2hyXkZGOvsXY3<`JdzgqfTn@rr-{$DSzlabOZ0${%_U z+razZ)Z$O?ziFdQ;r(yI=zkjd@D03Q_p-TpBLBCG=Ks`t#0K8~Mk_zZ9{NpYY>L5O zv*izCAGv}5(DjIqqL4NX^R3zPhX9~90$@GvgKi@7TeIa40g&DZfc5APx{3I2&6Ym| z0KE|a>oFko+l|!b;DG-E2Vyn?U_BC~h^I}%0c*DWTLeh|P4NfaNd3XqV?o#^V!<_A z{uF_Y6d-Ip8l;Hn%@YIbh`_%c0~@J8*m^t&+eAFLX3L+(z(z_CwjL3}HW3l7+483d zY@`NZ>oFmG6EWeMEq{u@MtTsw9uvYh5fiT2@}~%FBnaW_F(G^tG2xmme~Q3HiV(gY z6T&wU6Rz3vrwD8$3E}H8A$$`t;hHUfioiyi5WXH0A~q2duG#X32*5VdgoyQ+5V47v zaLtxKMPMUMh**ya5u1n!*KGMy1UAxyi1nBdv5A;)&6Yn!U?WY4SdR%2n}`Y5Z240J zHqwNM^_UR3iI{NBmOn*cBTa~0j|nMB5}W4UaLtxKMPMUMh+K~ek(-DK*KGMy1UAxy z$n}^Ixrvx?&6Yn!U?WY4T#pHnn}`Y5Z240JHqwO1-Q)2;8M7L)vR{KXXFGv7U$WW*|V`CHpeda&$z?i{(ZKoNv3vyVVzmzA*tazi76wI-Xe>P$1rr+}rdhKwl;iyiLgMKoN)Sax&9 zYGj|u{W|5?B;*(uKJCXaYpdFnqdEYPJ1OKYU~(w~7vLwlxZj>Tf4O|__-ySK7C`c9 zi|RUy@F?n4^?L-XYEkp@)(Nxk(FMm)(ra7^-!krONJM)BcgOvWpRqF86)OvNiXlB8 z=|8lKQ&#Uu1TrPDN|ln9vtJcU%~ylMU>K-0426J#Ajo|X$Ubo}CFN+H0uYEecr7~) zPRUjR8@OALsYyL#k{4M7hI4Ts*gE}s2CW{!^q@uZ2p~H?+AyTNE&B~eNMn3_<8btg zuDcv^Qh@+kK*1HJ+V!0N?dnQ#8is;xPPm4dW4`pGs1A0M_zTW_oEVfAA+VGnV4s| zx3u|1a73K=OD(bJJ6}5RBjl634^Ql_rsfq+t>)mM9xBOcG=Ho-v+K;ls@%tVjZc2Q``WQc6Zg1$I z1u(^1YlnWgVYV&aS0hl?YDFf=_(1iDKVS!u8BoRm@FSR(RRcbT?Eow(1I!(<0ORon zO#anBCq1Nx$@!~Fb4o#=>ri0z%c>V=6c+hJRC_nNaVhw2w<*wC*Iq`Po&K=+z}!oY6?|VA=)urHWoJwQnJ{R6kos*Ho_~qM^6`@z>h4k zZRvfQ9ID*CRe1%YZlcFx-T`_o;*?T~VSiOQQKXu7g~uQ&!IgxFvLR zw}=(!U&&(x6&Gwljwf%`5@zB!ydyk5%Y{{?a~$_{#u;NL{+!hZzi+?$HWn>0%__+S{B~9bxDDKh>Wva- zRIh)nsmr3sA@ucSG*{StlTcAT@d$MW<7gG(Epg_T099eZP zYjma*0IBY?zSwLvm0!rCy~|Or!{0~au7czh=oQHpk(W3({izw|is0g-mIHh@6f5P& zU&eH*T~6A=JXB9tl}B;}BjTN=^DJ%>R5_As+l^EF1ojTna^km5ynh`f7PswKt)NFG zzq)|Od~Hf`jEfNH_Lm87mUO?%y+=E-aVuS`u9^K(CztsN4X^ww3CA1QR`%iobfnQI zzi4U=_?85i$tZ>4A6D zkj!bfw6>|6KFJG4M*YSkS4`6+I2{#w6P6QKk?kCgeAWFXu*&(fMgBl|RF#F2g^Ee8 z9O322h`_~j>33sv1!j-RaqT>wd#mEKPEobTv+kDY-M8yv+%WPUp{Ob|P|1}RI_3+K zSG&GAnert(6)-Kkd$O^;Sl~JRz5F$AlAJajtx)iH~&7aq|v}!B?goxsCDY ztC^G?^_MNY4Sd(Ow6)lpqtw2u=ek7a3l*mrkGLGhq24C=>^^@9R6p0^2M=rbtyiHk z0*tG?#9esIO+%gU#7W% z?&b0Ma0D;(Ag_I;v#0r`K-XS}PsaMPmQ~mxhg{!- z+F$D1i7=Sm5}h3jXMQb*@mWfC?7i(-*BnvzEHSX{v)SAjX8YNBtSQq84_};Q3@874 zzHvVTsS&LJ8PFAFnd+PY*~&uEoJsBa@fe+?I}cosH9nw^fQeM8Wi=QLitY2bS&83^ zJfCRQMQ2pCs%a@bS@p%wgZw});?NZ~>kpk@#XlRpYrU=#)!A=q=ReT7i+r>BL1B%| zQpdYABXV1Z%n>F>rbyb{?PFA67{C9LNiN^ZA=I^h`+D8-4R=Q;UuGE*8d zhi2;r)EGw&PMDmud5^N47%#bdf4&e(q?I8L98AQy-(20386_(>eLBE&|BUg3ZqVp0 z%adj=r9bNt4>nz(DQ24;jcIE?^j0brjulIM&y{QPB|3u~-+nzDaS>^G^qTcBSo2e` zgHY8inYzcm-o^Is?wS){HaFr`N*39eS!bmOb~|JmyQ9pmTPfa1jV---0BiDY3PwzO zgjIS({`@#l>iBCz1-MV`tWWM-^r*aPf8zzKwtLw<&*kULxjpPhs!MvrGGg{-rF}Ia z<`lL0V+Pz0?5T7>%f35mcyAA9kh{LJySnD~CknQVFIdRrSJ?()chAW1yy{?JPfdEA z!Sg{f(pvn@g(3OFps}k5(R+`lJd*>rTOvxGXr>4Ih>dN^4AM`7w7a4T%9Dn$+po1gt=fm3w#ygFT;-XjVO=q`g!=^dlBcAlaMOrGgot4Bg^ip6UUp;j)vB?vfP&>WT5 zt3GjmZf>r&cZ}OCBm>s|Tw$eerWW-)v1NNOe7LCh^St6wI@pmbCA$ZMp0!&LYscEG zE;n=?*kM?9yQ+VooF^}B!m8xBXg7UlR#gSLtul3&@D?50a{ zRdIH7aduiu!l%AVLgoh8!`}y`f>9KgJYmSCA$Vy zwt|gpEvYZU{Pv=Vdc@re;OOCJ>`gpUg6MKWLnxk=@*&#TEWA#+|M)Qk7sqY6bDNoH zP7F8QY#csEU8E#&T%3mlju@S?nhKaM4>{tJlq7TJByA^~K1l%`ia{7?g&_&3pU0f24jdgyTl@fMDe%XibeE$7sg83^l% zu!&_)=cKIe&I?&bL;1%b7Rct?BOln@Vr!Kv%iI>etOgCHElyr!5Poy(_JRDHbtX%$ zd_@hf&sxf8P2A(Mzx3>J;=^YF3%!FRZ~uknOqbwK(d|!V&at@n7(LL<$8{Us87&ot zWEAdYFA^ddh$Mm^o(UJU1_o^#IH8T-o?QBzP5ay>-hz*XPh;>;s;(4s_?^WwN&xqq z4fB>28nvDlT>Vm|Hk})gTs71s80ypXB)Zqw+PD>7jON$OYQN)pmD$k1DCLNLazg{b zdpjG)5P?SLfUNrYd!J@qry~#*y7Zr1IV2gLC7ok+(-Xg~d7VqGdIu$?<^&I) z(eJu)RYGYm5snOz=wy!IKGuu}XPn!9*3i+m^!{9V!w?~k;YnP0;GWEp5(B^5^a8^5 zcf;}oOBLo;S{%>`8oCVd!U@bXr#)YlIMVtKtfViB8KM!1cProd z$rg%tYYvTgUysq_-Y44D&(faaW-RHML5t&jDtL1)*vv`=d$OOr z?yK~?wlrR5hh{$U2K?kZz3PuGQ)NoVdY%{fRt9$0-Z=g`{>{qG8Hcbcrn+^)wad z`z%_Iv;VSZeZou1qZwf_rcU3CqyW-%{)L2mv|=;Mpc2usV)U)Cxb!ea&rLwNJS6U* zk-F$R6`_!b3n{mAiQ%V7n#$bxKI4RftAIW((;FuWxkf&zw<%WG z!*K~>6K~Yxn#U8Bm7bQiO3`i;P2nR8PfP9c3NJtFs3hFcP#32Oz0zSfitZqCObDac zcdCEPJS6H-r^(bxzZ`$GdfKLUXurUn2&*~G5pPNE23qzuDO`PK`OZ=i4XMLL@t+`3 zWS!i4{>*YVk4mIVJ=_o#6F$-T<&@ao3yYo5>kMT+z1`k{M17G?eod{SD<4JabI#D5 zQ}z_$hHi=G~!>+N)HT-BCKKsT(nJVNptstXA18zJ{60uW! zB*-0MsA2P&rf_bEsXr6R|L%xIfg4nxwhFR#azIUkT=jNaU(YPdyagcVU@ zxok&|4qjlcSpQDj80V10=LPm8FIlztg2e}pqjQgEjO7<{4>Qk6l;|D89h>tfFV}T) z`HMa-y`bJ}AY|JdLP7^COI;LuDvMx`e_Lp9k!cufMY-k2zA2T*+)5yQk}yp7?Qjdj zjRz+7y%3h*d%7KpRWad;XjqL5UJN3_+3r&O5qt1am|qIw!^ zBgY=Rxg4@{BpG>>-m@Pkn=0g9!+QD(vsZ@ZOO4hEi`EcFO{sf{CZI|O(5eak0lM#? zXz_iosD->d`05@09`@7OLzND~_sulVKf^;Lk5w}ih}$kMUuP1_Y~~1gANO`iCUv!J z4~w?#c>Hx7q)l&+ix*2K@7>H>Qf2o!o2L1qmdXXs@weM`wMZf80!mdids=gRePP>= zhMPPboR|t&9y%;~T;ihe;)k0wLt4V^-u^SkE|y4(w0Gy}(tOSz>`%u{9M9SNadEOo zj^~WDgdV+fvC|_WYTnZSQ)<(PP__o^>`1Fe#$D(GJ`qyh!?kx+zg`V+KY#G*zGVOW zn{SRuMjVtpS~Fxn*w603dJ@=W?blq%JSE<7F~F-?!`3b8AkSo0vINvz>7H>}%MNNV^NB8fY&emDTUvRUrc|0lnsU5$?4jRFNX%EIP;!0@ zORe+yR+H7Q?}1u9}z+7s8zGK5%&PV9Xw!sM*UH>mHEl zzEHM0YQ=O+iXntSxVuWUqVz)eJq)Cd|0UiD^THd~aqNV&o{sY5bk5+HJZ&T~B zKObl~U{8BvUvEtw^Bb-zqk)yt8*kjtx1MZ_biO)x$%`0t=$7)19R9AhCmPetvlwy3 z3%xbc$D|HQ%Wun3wQtS3Y$foFXJ^gZB(W8JiTBZ6W@kGM73{^lc}dcdSzpye4o@DZ z8U6HWVuny({|qP+bGftWB--+|Wx+Yd;IwnDV-0%L4{`>vO?C&xUj-k9!`s?}Rc<)2U1_%q_aob1`A~ydCjIXI8(Ky@e zvUcQ_>bstW3J49Z9rqjrsG{}nf9h6S-p`j`8FM|f_{Hbw)CKWU3s>y4wr1Ol&$qIh z{NqWyRyKtp7pKFH98$gA;%(!*5Zib0wNA)xy;$VsT}w5^ zT@C%E)+biEZgii}h#itm43BD+d|MOTm=5yO2`K8GZk9b2-F3-8t7=#M65b?f(DAwN z)k^WXo0(C^7Tu)u6yMOK4Ad@!Eo9ZR;c_JXKXI479bB z6E;HPA$S)Rl8vppk1NT*N86C-V^73b3MjG7rT}eQs6aqwqK#)Lbv=kH}4Tqs5DO)K}Xb6G^ z!{8t&91EfT{n*bySf0AIfvl^g6;@vr_q{vHH#uP>8+S277vXN_oPioL#NKl$&S_ z7y<*sU?55fkejy?ncxL-aufO1$d7hZNp3_}TNmnD7LeL5!NS>{EGH~X?da#{yI+nj zKRa@AlUnmpisPsI^L`Ga8$p_IoCNGy!HJVklzAPfGtlD~Xi zYh!I~kMx%|P*?;8OMQ~@tBv1E{#zSAg6l7>tY^vhEg06y{??8q5o_h_>PR5V6I@&z zY>8_Nj$ju{D_QWbpMER91{qe>ndnZ1Rzp?Z-QCs_OM+P;h!Bhgh-gK$0wFBnB#;Hd z3IT#yB1skmX%Y&9f~_Hf$NygUFYRhLQMTG}B9eZzi-1X!5F{%M2x@_ZfDj~z6$oQR zv;d(|Rg# z|CUR-PJ-9BjgVl!mU2+kJVL~ON+S$@taPXW!nN(AU!tc27(Wy5#Ez4)1D|e5&M#9j zQjxh<@t9@wf)R1$eiWy;?9-Zo<$W=rLeb)IceNyqnba$L8^jz@)AJLu^iMDQp{v_` ztIm=SNgTdtcV41q;8N60x|*_Pk{W5fxJ6tEfiVQ>GVO?bcGz4 zmY2)JBLiyg5=Y?3IW9z9-iPd*uPP?8R-jrr%2+x~O;> zngOw=2|o|80Evd2N822Y2JWOK?Bq;d=2mxErR6Z|2-+G2Irn;zE*?)?Xv)I!D5jlK iLgeVyej_btQ!Rip#F=$_r`Eymw>|ZsP}I*|_5L3l*VhLC literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/enc-XI-base.pdf b/qpdf/qtest/qpdf/enc-XI-base.pdf new file mode 100644 index 0000000000000000000000000000000000000000..695c463a5069405e292d038d05a031a5aabcf7d8 GIT binary patch literal 14041 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 B2=M>_ literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/enc-XI-long-password.pdf b/qpdf/qtest/qpdf/enc-XI-long-password.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f4f206ed63213880c904c521d7fce999859c271a GIT binary patch literal 10915 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 literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.3.out new file mode 100644 index 0000000..3795c64 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.3.out @@ -0,0 +1,4 @@ +version: 1.3 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.6.out new file mode 100644 index 0000000..762063b --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.6.out @@ -0,0 +1,4 @@ +version: 1.6 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out new file mode 100644 index 0000000..9616863 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 1 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out new file mode 100644 index 0000000..4571bf2 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out new file mode 100644 index 0000000..7632120 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out new file mode 100644 index 0000000..7823a1d --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out new file mode 100644 index 0000000..2d78af7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.pdf new file mode 100644 index 0000000000000000000000000000000000000000..7dbab51619b8f9f3e9883dbafb3f23a3f8c408d2 GIT binary patch literal 865 zcmZWnOODe(5alDJV2L{v64?QEcl#?v5tB(QA_fRa1dAw(j#CLn9yhW(Ltw=PxB)AU zzy;W5gpgRldbk8TM71+cMl-f0cURS`s`sjzgKqqQJq)epx4&QhSqvOdvo}_&1$J+( zF)ve*#o)65;)}?f<1o!7QsH0T4riV zZVU6dDP`Ps$wRfuH88NBr1KIcAS&pz257tYVdcKEg93#1tz9)-Odr_OZ^&L zQYTI`jRpNPsj-WRnpdDw7buUd_2<$$8xDTH6Jv+JX!LJ>{qgDJ=F#1Iw>G~T8@qr1 z^!K=N`lq;acH`Rj>$f+g+Bx!EKhtJi)r_X1U_Zv}1a+E7nodq7(|if`t295LiS7;0 zz~Hoaqma#M6)?hWFi*+i2KAQjDkpT5xPG2T_HljgUD-7%^s9m Q7P^wb3j?d!>^+aI|1InAJOBUy literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf new file mode 100644 index 0000000..efd08ba --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.5.qdf @@ -0,0 +1,107 @@ +%PDF-1.8 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Extensions << + /ADBE << + /BaseVersion /1.8 + /ExtensionLevel 5 + >> + >> + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +%% Original object ID: 6 0 +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 7 0 +7 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 8 +0000000000 65535 f +0000000052 00000 n +0000000223 00000 n +0000000332 00000 n +0000000574 00000 n +0000000673 00000 n +0000000719 00000 n +0000000864 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [<31415926535897932384626433832795>] +>> +startxref +899 +%%EOF diff --git a/qpdf/qtest/qpdf/extensions-adbe-force-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-force-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.3.out new file mode 100644 index 0000000..4571bf2 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.6.out new file mode 100644 index 0000000..4571bf2 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.6.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out new file mode 100644 index 0000000..4571bf2 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out new file mode 100644 index 0000000..4571bf2 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out new file mode 100644 index 0000000..7632120 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out new file mode 100644 index 0000000..7823a1d --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out new file mode 100644 index 0000000..2d78af7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-min-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-min-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out new file mode 100644 index 0000000..1446741 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.3.out @@ -0,0 +1,4 @@ +version: 1.3 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out new file mode 100644 index 0000000..0cc726a --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.6.out @@ -0,0 +1,4 @@ +version: 1.6 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out new file mode 100644 index 0000000..a714b14 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 1 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out new file mode 100644 index 0000000..ed0bf57 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out new file mode 100644 index 0000000..f13ea01 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out new file mode 100644 index 0000000..00858aa --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out new file mode 100644 index 0000000..dcf87e8 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.pdf new file mode 100644 index 0000000000000000000000000000000000000000..39ea71ef6ddfcdcb4d43ef99246dfa4fe968513b GIT binary patch literal 923 zcmZWn%Z}496y+hL5{ZAfNOT92hwVJ1s$x26M8yE5tzc1g;kHf*%ETyfhr)^<;15{w z3H$)tjt~+nSPwtJ4sq>H+KM`h*!TMQo^$SP_T%J%dlXvDuYW%OwOnvOPG4KC7TDdD z5=E8Cq5^$9jyqkm>F`Rt5T(w5jnKWDr{Y}Xz@Xg*yDt@2@(@9eT(YrSS>=UkdE*F@#L^0PSb-E^dBA0~%7wpH`yn-cNO=}=3xh!YMQ`?hB%$SXf258Ca?ts#|{-(GAkBfzs!nboP}NBDL=!WBxv|>IaQ`XS{a@<0?k`HzqNi{YK$hv30@K_ z1(Ujwt5T$v1A*mi7x;`31}SV~F4ENtifzmd!-JSdgM%1}u#x{^nRAHs_iLz22c8i#4_k2_nMnOb8 g5>nsuDIp>8f{3*zdJZbZOJ!{EDYKf*?z6=D2YDd@N&o-= literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf new file mode 100644 index 0000000..e391af0 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.5.qdf @@ -0,0 +1,111 @@ +%PDF-1.8 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Extensions << + /ADBE << + /BaseVersion /1.8 + /ExtensionLevel 5 + >> + /Potato << + /BaseVersion /3.14159 + /ExtensionLevel 16059 + >> + >> + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +%% Original object ID: 6 0 +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 7 0 +7 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 8 +0000000000 65535 f +0000000052 00000 n +0000000301 00000 n +0000000410 00000 n +0000000652 00000 n +0000000751 00000 n +0000000797 00000 n +0000000942 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [<484577389048fa45fc00a1f5b434efa5><31415926535897932384626433832795>] +>> +startxref +977 +%%EOF diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-force-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out new file mode 100644 index 0000000..ed0bf57 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out new file mode 100644 index 0000000..ed0bf57 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.6.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out new file mode 100644 index 0000000..ed0bf57 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out new file mode 100644 index 0000000..ed0bf57 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out new file mode 100644 index 0000000..f13ea01 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out new file mode 100644 index 0000000..00858aa --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out new file mode 100644 index 0000000..dcf87e8 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other-min-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-adbe-other.pdf b/qpdf/qtest/qpdf/extensions-adbe-other.pdf new file mode 100644 index 0000000..367f376 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe-other.pdf @@ -0,0 +1,104 @@ +%PDF-1.7 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog + /Extensions << + /ADBE 8 0 R + /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> + >> +>> +endobj + +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +7 0 obj +[ + /PDF + /Text +] +endobj + +8 0 obj +<< /BaseVersion /1.7 /ExtensionLevel 2 >> +endobj + +xref +0 9 +0000000000 65535 f +0000000025 00000 n +0000000179 00000 n +0000000261 00000 n +0000000476 00000 n +0000000575 00000 n +0000000594 00000 n +0000000712 00000 n +0000000747 00000 n +trailer << + /Root 1 0 R + /Size 9 + /ID [<484577389048fa45fc00a1f5b434efa5><484577389048fa45fc00a1f5b434efa5>] +>> +startxref +805 +%%EOF diff --git a/qpdf/qtest/qpdf/extensions-adbe.pdf b/qpdf/qtest/qpdf/extensions-adbe.pdf new file mode 100644 index 0000000..4d13cf7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-adbe.pdf @@ -0,0 +1,106 @@ +%PDF-1.7 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog + /Extensions 8 0 R +>> +endobj + +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +7 0 obj +[ + /PDF + /Text +] +endobj + +8 0 obj +<< /ADBE 9 0 R >> +endobj + +9 0 obj +<< /BaseVersion /1.7 /ExtensionLevel 2 /URL (http://something.adobe.com) >> +endobj + +xref +0 10 +0000000000 65535 f +0000000025 00000 n +0000000099 00000 n +0000000181 00000 n +0000000396 00000 n +0000000495 00000 n +0000000514 00000 n +0000000632 00000 n +0000000667 00000 n +0000000701 00000 n +trailer << + /Root 1 0 R + /Size 10 + /ID [] +>> +startxref +793 +%%EOF diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.3.out b/qpdf/qtest/qpdf/extensions-none-force-1.3.out new file mode 100644 index 0000000..3795c64 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.3.out @@ -0,0 +1,4 @@ +version: 1.3 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.6.out b/qpdf/qtest/qpdf/extensions-none-force-1.6.out new file mode 100644 index 0000000..762063b --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.6.out @@ -0,0 +1,4 @@ +version: 1.6 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-none-force-1.7.1.out new file mode 100644 index 0000000..9616863 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 1 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-none-force-1.7.2.out new file mode 100644 index 0000000..234701c --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-none-force-1.7.3.out new file mode 100644 index 0000000..7632120 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.0.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.2.out new file mode 100644 index 0000000..7823a1d --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.out new file mode 100644 index 0000000..2d78af7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.pdf new file mode 100644 index 0000000000000000000000000000000000000000..52f5623eb8704444ad9071bbc439f367eccc0120 GIT binary patch literal 865 zcmah{%Wl&^6y+hLFcSZ8k;o44WBin&h^CI!hyo#rU=d|8O>Tpc>#6KXaar*L`~fRI zfgfO-3L&wAb@>T)h&xUlSM37Zl4tJooO^D27{!mQ#{p}9{qy-Rv%my-@RoHtVDw9c zS&_)B0Dawwdj0Cq69vA)TzkNv)Ju{3c!DXo(CzBa7>XqpU{lQz7?aau1Y=hyk;)~x z8DsdDV zp$$}PC~^v{VU4hmt9(v4HOW|#r#e_fL*uS^V-OA*1nJVxl|OaP~1sh6wT#&Pl3oyWM{gvwwT??cx9c literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf new file mode 100644 index 0000000..230c1c0 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.5.qdf @@ -0,0 +1,107 @@ +%PDF-1.8 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Extensions << + /ADBE << + /BaseVersion /1.8 + /ExtensionLevel 5 + >> + >> + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +%% Original object ID: 6 0 +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 5 0 +7 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 8 +0000000000 65535 f +0000000052 00000 n +0000000223 00000 n +0000000332 00000 n +0000000574 00000 n +0000000673 00000 n +0000000719 00000 n +0000000864 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] +>> +startxref +899 +%%EOF diff --git a/qpdf/qtest/qpdf/extensions-none-force-1.8.out b/qpdf/qtest/qpdf/extensions-none-force-1.8.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-force-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.3.out b/qpdf/qtest/qpdf/extensions-none-min-1.3.out new file mode 100644 index 0000000..3795c64 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.3.out @@ -0,0 +1,4 @@ +version: 1.3 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.6.out b/qpdf/qtest/qpdf/extensions-none-min-1.6.out new file mode 100644 index 0000000..762063b --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.6.out @@ -0,0 +1,4 @@ +version: 1.6 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-none-min-1.7.1.out new file mode 100644 index 0000000..9616863 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 1 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-none-min-1.7.2.out new file mode 100644 index 0000000..234701c --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-none-min-1.7.3.out new file mode 100644 index 0000000..7632120 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.0.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.2.out new file mode 100644 index 0000000..7823a1d --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.5.out new file mode 100644 index 0000000..2d78af7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-none-min-1.8.out b/qpdf/qtest/qpdf/extensions-none-min-1.8.out new file mode 100644 index 0000000..491fdb7 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-none-min-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +null +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.3.out b/qpdf/qtest/qpdf/extensions-other-force-1.3.out new file mode 100644 index 0000000..1446741 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.3.out @@ -0,0 +1,4 @@ +version: 1.3 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.6.out b/qpdf/qtest/qpdf/extensions-other-force-1.6.out new file mode 100644 index 0000000..0cc726a --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.6.out @@ -0,0 +1,4 @@ +version: 1.6 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.7.1.out b/qpdf/qtest/qpdf/extensions-other-force-1.7.1.out new file mode 100644 index 0000000..a714b14 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 1 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.7.2.out b/qpdf/qtest/qpdf/extensions-other-force-1.7.2.out new file mode 100644 index 0000000..ed0bf57 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.7.3.out b/qpdf/qtest/qpdf/extensions-other-force-1.7.3.out new file mode 100644 index 0000000..f13ea01 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.0.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.0.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.2.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.2.out new file mode 100644 index 0000000..00858aa --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.5.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.out new file mode 100644 index 0000000..dcf87e8 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdf b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8bf4fe068215d9ad8baffdf14efe5666ab54253e GIT binary patch literal 923 zcmZWn&5qMB5avfnB@*v2Nc0AqI8K~Isw%eIMpP_N+6oR;58iaP1a)JSxJ%*03-AV< zcm!U6YgY(~6P%Zq;D#8x+aFQq5PQa-=li~ydas>6Ug2W4Xn@;U zXe_E+6&0A{VY}6_o0hEb1(qfQEt9=ANU;zu0JroljMG0xa za*7rB#HJ7K@ZB2(_eg3vQ&UPiSWFB_lU|cOQu9Is1NU)0sbB=$LdR=x&%U>Xn)DW0 zkWgJguqyWiC%J5?1-;_YCzuaW;sd?alO49#^_(^MFiuBFEb-5MjteIE77&5nZ#O*u+>I@5THe+Kcgo8u=eK^!Bl0w}vidJ|p){ zPy0%l;j+^>$loJG;C9}7tl!R@sBg4D!GlyINN ff$&AZdCdJN37cax2bGqkwl;_$bn5lav()(qXE^}l literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf new file mode 100644 index 0000000..a4ffae3 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.5.qdf @@ -0,0 +1,111 @@ +%PDF-1.8 +%¿÷¢þ +%QDF-1.0 + +%% Original object ID: 1 0 +1 0 obj +<< + /Extensions << + /ADBE << + /BaseVersion /1.8 + /ExtensionLevel 5 + >> + /Potato << + /BaseVersion /3.14159 + /ExtensionLevel 16059 + >> + >> + /Pages 2 0 R + /Type /Catalog +>> +endobj + +%% Original object ID: 2 0 +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +%% Original object ID: 3 0 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +%% Original object ID: 4 0 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +%% Original object ID: 6 0 +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +%% Original object ID: 7 0 +7 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 8 +0000000000 65535 f +0000000052 00000 n +0000000301 00000 n +0000000410 00000 n +0000000652 00000 n +0000000751 00000 n +0000000797 00000 n +0000000942 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [<369e89600ee1a6c4c7e73533610180c2><31415926535897932384626433832795>] +>> +startxref +977 +%%EOF diff --git a/qpdf/qtest/qpdf/extensions-other-force-1.8.out b/qpdf/qtest/qpdf/extensions-other-force-1.8.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-force-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.3.out b/qpdf/qtest/qpdf/extensions-other-min-1.3.out new file mode 100644 index 0000000..0f57677 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.3.out @@ -0,0 +1,4 @@ +version: 1.5 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.6.out b/qpdf/qtest/qpdf/extensions-other-min-1.6.out new file mode 100644 index 0000000..0cc726a --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.6.out @@ -0,0 +1,4 @@ +version: 1.6 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.7.1.out b/qpdf/qtest/qpdf/extensions-other-min-1.7.1.out new file mode 100644 index 0000000..a714b14 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.7.1.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 1 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 1 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.7.2.out b/qpdf/qtest/qpdf/extensions-other-min-1.7.2.out new file mode 100644 index 0000000..ed0bf57 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.7.2.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 2 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.7.3.out b/qpdf/qtest/qpdf/extensions-other-min-1.7.3.out new file mode 100644 index 0000000..f13ea01 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.7.3.out @@ -0,0 +1,4 @@ +version: 1.7 +extension level: 3 +<< /ADBE << /BaseVersion /1.7 /ExtensionLevel 3 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.0.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.0.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.0.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.2.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.2.out new file mode 100644 index 0000000..00858aa --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.2.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 2 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 2 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.5.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.5.out new file mode 100644 index 0000000..dcf87e8 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.5.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 5 +<< /ADBE << /BaseVersion /1.8 /ExtensionLevel 5 >> /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other-min-1.8.out b/qpdf/qtest/qpdf/extensions-other-min-1.8.out new file mode 100644 index 0000000..d121666 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other-min-1.8.out @@ -0,0 +1,4 @@ +version: 1.8 +extension level: 0 +<< /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> >> +test 34 done diff --git a/qpdf/qtest/qpdf/extensions-other.pdf b/qpdf/qtest/qpdf/extensions-other.pdf new file mode 100644 index 0000000..e863981 --- /dev/null +++ b/qpdf/qtest/qpdf/extensions-other.pdf @@ -0,0 +1,98 @@ +%PDF-1.5 +%¿÷¢þ +%QDF-1.0 + +1 0 obj +<< + /Pages 2 0 R + /Type /Catalog + /Extensions << + /Potato << /BaseVersion /3.14159 /ExtensionLevel 16059 >> + >> +>> +endobj + +2 0 obj +<< + /Count 1 + /Kids [ + 3 0 R + ] + /Type /Pages +>> +endobj + +%% Page 1 +3 0 obj +<< + /Contents 4 0 R + /MediaBox [ + 0 + 0 + 612 + 792 + ] + /Parent 2 0 R + /Resources << + /Font << + /F1 6 0 R + >> + /ProcSet 7 0 R + >> + /Type /Page +>> +endobj + +%% Contents for page 1 +4 0 obj +<< + /Length 5 0 R +>> +stream +BT + /F1 24 Tf + 72 720 Td + (Potato) Tj +ET +endstream +endobj + +5 0 obj +44 +endobj + +6 0 obj +<< + /BaseFont /Helvetica + /Encoding /WinAnsiEncoding + /Name /F1 + /Subtype /Type1 + /Type /Font +>> +endobj + +7 0 obj +[ + /PDF + /Text +] +endobj + +xref +0 8 +0000000000 65535 f +0000000025 00000 n +0000000163 00000 n +0000000245 00000 n +0000000460 00000 n +0000000559 00000 n +0000000578 00000 n +0000000696 00000 n +trailer << + /Root 1 0 R + /Size 8 + /ID [<369e89600ee1a6c4c7e73533610180c2><369e89600ee1a6c4c7e73533610180c2>] +>> +startxref +731 +%%EOF diff --git a/qpdf/qtest/qpdf/good13.qdf b/qpdf/qtest/qpdf/good13.qdf index f8993a8..36bd923 100644 --- a/qpdf/qtest/qpdf/good13.qdf +++ b/qpdf/qtest/qpdf/good13.qdf @@ -18,7 +18,7 @@ endobj <01020300040560> (AB) ] - /indirect (hello) + /indirect 4 0 R /nesting << /a [ 1 @@ -58,17 +58,22 @@ endobj << /Count 1 /Kids [ - 4 0 R + 5 0 R ] /Type /Pages >> endobj +%% Original object ID: 8 0 +4 0 obj +(hello) +endobj + %% Page 1 %% Original object ID: 3 0 -4 0 obj +5 0 obj << - /Contents 5 0 R + /Contents 6 0 R /MediaBox [ 0 0 @@ -78,9 +83,9 @@ endobj /Parent 3 0 R /Resources << /Font << - /F1 7 0 R + /F1 8 0 R >> - /ProcSet 8 0 R + /ProcSet 9 0 R >> /Type /Page >> @@ -88,9 +93,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -5 0 obj +6 0 obj << - /Length 6 0 R + /Length 7 0 R >> stream BT @@ -101,12 +106,12 @@ ET endstream endobj -6 0 obj +7 0 obj 44 endobj %% Original object ID: 6 0 -7 0 obj +8 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -117,7 +122,7 @@ endobj endobj %% Original object ID: 5 0 -8 0 obj +9 0 obj [ /PDF /Text @@ -125,22 +130,23 @@ endobj endobj xref -0 9 +0 10 0000000000 65535 f 0000000052 00000 n 0000000133 00000 n -0000000578 00000 n -0000000687 00000 n -0000000929 00000 n -0000001028 00000 n -0000001074 00000 n -0000001219 00000 n +0000000576 00000 n +0000000675 00000 n +0000000736 00000 n +0000000978 00000 n +0000001077 00000 n +0000001123 00000 n +0000001268 00000 n trailer << /QTest 2 0 R /Root 1 0 R - /Size 9 + /Size 10 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> startxref -1254 +1303 %%EOF diff --git a/qpdf/qtest/qpdf/good5.qdf b/qpdf/qtest/qpdf/good5.qdf index e830781..b868de8 100644 --- a/qpdf/qtest/qpdf/good5.qdf +++ b/qpdf/qtest/qpdf/good5.qdf @@ -5,17 +5,22 @@ %% Original object ID: 1 0 1 0 obj << - /Pages 2 0 R + /Pages 3 0 R /Type /Catalog >> endobj -%% Original object ID: 2 0 +%% Original object ID: 7 0 2 0 obj +true +endobj + +%% Original object ID: 2 0 +3 0 obj << /Count 1 /Kids [ - 3 0 R + 4 0 R ] /Type /Pages >> @@ -23,21 +28,21 @@ endobj %% Page 1 %% Original object ID: 3 0 -3 0 obj +4 0 obj << - /Contents 4 0 R + /Contents 5 0 R /MediaBox [ 0 0 612 792 ] - /Parent 2 0 R + /Parent 3 0 R /Resources << /Font << - /F1 6 0 R + /F1 7 0 R >> - /ProcSet 7 0 R + /ProcSet 8 0 R >> /Type /Page >> @@ -45,9 +50,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -4 0 obj +5 0 obj << - /Length 5 0 R + /Length 6 0 R >> stream BT @@ -58,12 +63,12 @@ ET endstream endobj -5 0 obj +6 0 obj 44 endobj %% Original object ID: 6 0 -6 0 obj +7 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -74,7 +79,7 @@ endobj endobj %% Original object ID: 5 0 -7 0 obj +8 0 obj [ /PDF /Text @@ -82,21 +87,22 @@ endobj endobj xref -0 8 +0 9 0000000000 65535 f 0000000052 00000 n 0000000133 00000 n -0000000242 00000 n -0000000484 00000 n -0000000583 00000 n -0000000629 00000 n -0000000774 00000 n +0000000181 00000 n +0000000290 00000 n +0000000532 00000 n +0000000631 00000 n +0000000677 00000 n +0000000822 00000 n trailer << - /QTest true + /QTest 2 0 R /Root 1 0 R - /Size 8 + /Size 9 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> startxref -809 +857 %%EOF diff --git a/qpdf/qtest/qpdf/good8.qdf b/qpdf/qtest/qpdf/good8.qdf index a8f5297..0eac9f7 100644 --- a/qpdf/qtest/qpdf/good8.qdf +++ b/qpdf/qtest/qpdf/good8.qdf @@ -5,17 +5,22 @@ %% Original object ID: 1 0 1 0 obj << - /Pages 2 0 R + /Pages 3 0 R /Type /Catalog >> endobj -%% Original object ID: 2 0 +%% Original object ID: 7 0 2 0 obj +3.14159 +endobj + +%% Original object ID: 2 0 +3 0 obj << /Count 1 /Kids [ - 3 0 R + 4 0 R ] /Type /Pages >> @@ -23,21 +28,21 @@ endobj %% Page 1 %% Original object ID: 3 0 -3 0 obj +4 0 obj << - /Contents 4 0 R + /Contents 5 0 R /MediaBox [ 0 0 612 792 ] - /Parent 2 0 R + /Parent 3 0 R /Resources << /Font << - /F1 6 0 R + /F1 7 0 R >> - /ProcSet 7 0 R + /ProcSet 8 0 R >> /Type /Page >> @@ -45,9 +50,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -4 0 obj +5 0 obj << - /Length 5 0 R + /Length 6 0 R >> stream BT @@ -58,12 +63,12 @@ ET endstream endobj -5 0 obj +6 0 obj 44 endobj %% Original object ID: 6 0 -6 0 obj +7 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -74,7 +79,7 @@ endobj endobj %% Original object ID: 5 0 -7 0 obj +8 0 obj [ /PDF /Text @@ -82,21 +87,22 @@ endobj endobj xref -0 8 +0 9 0000000000 65535 f 0000000052 00000 n 0000000133 00000 n -0000000242 00000 n -0000000484 00000 n -0000000583 00000 n -0000000629 00000 n -0000000774 00000 n +0000000184 00000 n +0000000293 00000 n +0000000535 00000 n +0000000634 00000 n +0000000680 00000 n +0000000825 00000 n trailer << - /QTest 3.14159 + /QTest 2 0 R /Root 1 0 R - /Size 8 + /Size 9 /ID [<31415926535897932384626433832795><31415926535897932384626433832795>] >> startxref -809 +860 %%EOF diff --git a/qpdf/qtest/qpdf/leading-junk.out b/qpdf/qtest/qpdf/leading-junk.out new file mode 100644 index 0000000..58847c9 --- /dev/null +++ b/qpdf/qtest/qpdf/leading-junk.out @@ -0,0 +1,17 @@ +checking leading-junk.pdf +PDF Version: 1.4 +R = 3 +P = -4 +User password = +extract for accessibility: allowed +extract for any purpose: allowed +print low resolution: allowed +print high resolution: allowed +modify document assembly: allowed +modify forms: allowed +modify annotations: allowed +modify other: allowed +modify anything: allowed +File is linearized +No syntax or stream encoding errors found; the file may still contain +errors that qpdf cannot detect diff --git a/qpdf/qtest/qpdf/leading-junk.pdf b/qpdf/qtest/qpdf/leading-junk.pdf new file mode 100644 index 0000000000000000000000000000000000000000..2b2a0a2cacac9a8d7c1455a9f1e8005323115d95 GIT binary patch literal 13670 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 literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/lin-special.disable.exp b/qpdf/qtest/qpdf/lin-special.disable.exp index 852c00831f388cf8d4eb0f2f87d6b3fc9849d7dc..14c2eaefd9a99eac17b2b2db9f183fb0dda308b2 100644 GIT binary patch literal 3092 zcmcguU5g_{6deW(*v$NZ6~T)zvVybSRh8-w9Kt#uVMTW*W*TG|2Bwo##@@|zYj++_thWZAMiy5LA+I!N_V;wKbF;yhorjt);;%}d+Y4<4!gVL z2D5g){q^x57V*G=ba2<&+XMT}I1y16e;`Ic@EJj?d&nqCOn`nXWS3GfKleR5q+C6pHVO1opVWH zUPM`;s_9reJ4eS|OXXrTayv~?2|Br6bSZKmh)t#%K6qDveGnDVB)zPNfnYrr66?m3 z0&&{iNmPhKF-%AJ&Ixc2^(K+R`iqCN&tCoT!OdrHv|7LXaP!gj$H8}xe!BMK!_#k`bAJ1M zbNiW%pHH`5lIqM06?d*vRC1ZxL7EgIDRQ7lz65kzjN)iNodI3o&2Ym4a9`xjTMU&CbzuSUIYw!hV30~cG1OEyUZ7tYqDRx-GH(Hbl=xvGfe z{UimuLc?-Y{v}{}xXI{=iEOV0T)jHW)(PV)k?-!OlhM=c8hITUC=1!k!CIYDXZYX2 zTiXQ|xbm)ETjiQ!h`&xV)!F^TCFE;MsPZtlsl^B_{Vp5f7uM zS%0Nld6urR980Rki(HnwdWusRjnNzNC`b3Cu9-f1DoGd7?ThT_@u)A+az3i(1{Ka> zjKmg^^hDR!k#-0bmi0sXt!Xs82gG%Fu=b2I0SaLr42x**o;_bA$IMxQ!D+ literal 3178 zcmcguOKjXk7+wWs6|4jy=ph9pehRrIv1h#FhZ?O)9;-%>X1iIbs-lQ^cPGKHvB7rQ zl!p`!^g@GB&plOu#0_yph)WLXp%MoKT&UEZIe`$D2=UK&y>{#k0%^gg-SK$l{~q(t zH&tJ(&5&7LnfmsRyMHRg01f=s8_L`qsIR&ncLMhv-T{LDKqsaJ>P1)wgBm~>gAGtu zf!Lrf0ihL~bO{)!O`xW3L$#`KuOk+N|NT++0=^~C*oxMB2m|>GJV?fvp%=ykwER5N zB84%_qWLkRX@&KOW>gCE1dq8r2-WO#ED9XA%L5Q9G<-h-Vxle$P}fimpvZAO;MWAr zT9I0Hc@fs<+T3Q0w3yLmj>+{FYiTXV*mATEwFtG1>YPq0gxSH6Cl zGZ4hyw^+^XMu=0bb)ASW^0wc>Z`OdB=r`_dN3Vlv##IE|*-`fHv^O_5NA8|0ZzxEp5T~BJM>YP%Q?u!aJ@r@9zOcMFqmT6X1kC z;06!<-Jp#WP_#=*fuJRWUoMx>*8|Bkq+kFQlK&p^Pe|KGPYV`8h@?&4!KiEII z@WM-@M?Z`msms&%u4vyk$L>7}CLWHI9-LFoBBqn*WL3$=HAE-vRU<&r$yjc+Ds1Jq z*LVap7BM}-a!&H`yc2S{fVzN)T^`J=wz}@yyQtlghZ?%xHmFxz?`1D^(}7FQ4o<-R z)V1AKB;6rAg8yTy$Q3g?iW0<*>sZ!e4QKJa2sV=GMWjGxLr8?!o@??ZeyejlQ_@;V09xKR!8I`uR6y zvY=$bjA##iY&}!pb6G$s_{thTZmI))FZ%A^QgPyk`*!^4?*zuOt+8O*loKJB7OW@%r?{~G9;0lIC7NN_6uIhPUZG@oVr@Nj*$mXiB~ z@@#=zSz*F%)+!`!=>xrzv?Mtv`8bv4BJW<~k=u5Lsf;|`Vy~Ee(f%a+{aQ};h+XXV zm>OPjJ0Zq5dCxU4)CnO;d`wWcw8@2;`%1n~P+`BvmGu5f@ih_O|H3Sdtp4#!_42OM zeiMkHF${{Q?2$d+s~#75vw7VInyDksAh=GF-Y`Ld!7#nRR+8vs2+b!ey@LJ}Pn!Hl z#X#`6F*YF4nFA7?RmmhZxrjz=v#UFk)pn(DMC|n1M7c(%WExIM6o(aVFsz%aHV&H9FkwiHd2mu4(m5#}-D{>=wI@ zHE@c3US_;vRd)?mVrGR~reT&VcE#XExnyyxWEf?G+ZD5VB&i#RfpA8esywZz%40>% znTSwPujCQ;5I-nt{#?l^&&W^_Im8vj6NsybJYoZ}iP(~*NK*deqG%B-vo5*i`@||S zO-<)SjENWHrRyb^H14Z?FdJd7k>VBv!7GGJky?A<^nC8w*9{Eg~AKR#H<*{Ezx(KJ%1`2BkG2x6X1n8&XySa zlb|P&C+!j=muzHV%XBl=5^T^=TI;`eZ62aWD&I~=78tKmye2w<+w?lQQ064X;`)d#UUqbilZv{ zuK^q278w>sY(nqC8OSuv$TS+BkZ#d=-H2a(d_%6Z$EV3&cP!q$-9!+zNN}Gp2{|$*IGt!=rZI&#NJv9ERLJi1d zL7{xDLS^I53}B8xnqd|}Vx^0=8v9Kw5{GzCvHN+!D-NI#Q>kCEIo%+d;lgGOEZ9cXpb A6#xJL delta 1011 zcmZ1|_Cs`np1y&Fg1(1Bw1T;zp@N}_nL@0BzQ2N@se-;MkZWP4pzo&uzTy0TvC(sQ&IyGi*kz1=Pq2?s?L))Ut_PUw5; zoD4U(62fuvQ4#M$CWd=TT#}P_F&i@*nHz(GVe%(t$@+>pYXfe39X62IcR#pB-(b@c z-`xi_EJ`_=X6HRPdc*bFzn+BY8T@kh?(NqMUe;vZJmK*4_ft-Ax&{25;Bh>F%P25F z&{;EtXX(!+f=BoG3cTbD>5BM#S0bvP?Xu>PLxL(tew}qzJk3=0GWxoM^PO9ZjsdCMRIl=%dU3iD*PXa|a;4tEf}3}gvv>Ty_Qd~A&5^(VKFz6C zYwvv{cQtcUkLA?!;a5ud-z>cU5^gG*a`M^J}Q;sIx?w-RoS<7f{xyG z0X>GhpSdI^$FR>K)1YL8L4Sa;X3VjTOoN2s2Hj)jlA5f-Nv?~65eD&bNl*UG8Ng(0 zN@8xB?8}v-49Z3fOloe|ig|o^dQLhtEfhMks$sKQVwd3wV+MxSJeKRgykk9?hg%c? De;bZo diff --git a/qpdf/qtest/qpdf/lin-special.preserve.exp b/qpdf/qtest/qpdf/lin-special.preserve.exp index 852c00831f388cf8d4eb0f2f87d6b3fc9849d7dc..14c2eaefd9a99eac17b2b2db9f183fb0dda308b2 100644 GIT binary patch literal 3092 zcmcguU5g_{6deW(*v$NZ6~T)zvVybSRh8-w9Kt#uVMTW*W*TG|2Bwo##@@|zYj++_thWZAMiy5LA+I!N_V;wKbF;yhorjt);;%}d+Y4<4!gVL z2D5g){q^x57V*G=ba2<&+XMT}I1y16e;`Ic@EJj?d&nqCOn`nXWS3GfKleR5q+C6pHVO1opVWH zUPM`;s_9reJ4eS|OXXrTayv~?2|Br6bSZKmh)t#%K6qDveGnDVB)zPNfnYrr66?m3 z0&&{iNmPhKF-%AJ&Ixc2^(K+R`iqCN&tCoT!OdrHv|7LXaP!gj$H8}xe!BMK!_#k`bAJ1M zbNiW%pHH`5lIqM06?d*vRC1ZxL7EgIDRQ7lz65kzjN)iNodI3o&2Ym4a9`xjTMU&CbzuSUIYw!hV30~cG1OEyUZ7tYqDRx-GH(Hbl=xvGfe z{UimuLc?-Y{v}{}xXI{=iEOV0T)jHW)(PV)k?-!OlhM=c8hITUC=1!k!CIYDXZYX2 zTiXQ|xbm)ETjiQ!h`&xV)!F^TCFE;MsPZtlsl^B_{Vp5f7uM zS%0Nld6urR980Rki(HnwdWusRjnNzNC`b3Cu9-f1DoGd7?ThT_@u)A+az3i(1{Ka> zjKmg^^hDR!k#-0bmi0sXt!Xs82gG%Fu=b2I0SaLr42x**o;_bA$IMxQ!D+ literal 3178 zcmcguOKjXk7+wWs6|4jy=ph9pehRrIv1h#FhZ?O)9;-%>X1iIbs-lQ^cPGKHvB7rQ zl!p`!^g@GB&plOu#0_yph)WLXp%MoKT&UEZIe`$D2=UK&y>{#k0%^gg-SK$l{~q(t zH&tJ(&5&7LnfmsRyMHRg01f=s8_L`qsIR&ncLMhv-T{LDKqsaJ>P1)wgBm~>gAGtu zf!Lrf0ihL~bO{)!O`xW3L$#`KuOk+N|NT++0=^~C*oxMB2m|>GJV?fvp%=ykwER5N zB84%_qWLkRX@&KOW>gCE1dq8r2-WO#ED9XA%L5Q9G<-h-Vxle$P}fimpvZAO;MWAr zT9I0Hc@fs<+T3Q0w3yLmj>+{FYiTXV*mATEwFtG1>YPq0gxSH6Cl zGZ4hyw^+^XMu=0bb)ASW^0wc>Z`OdB=r`_dN3Vlv##IE|*-`fHv^O_5NA8|0ZzxEp5T~BJM>YP%Q?u!aJ@r@9zOcMFqmT6X1kC z;06!<-Jp#WP_#=*fuJRWUoMx>*8|Bkq+kFQlK&p^Pe|KGPYV`8h@?&4!KiEII z@WM-@M?Z`msms&%u4vyk$L>7}CLWHI9-LFoBBqn*WL3$=HAE-vRU<&r$yjc+Ds1Jq z*LVap7BM}-a!&H`yc2S{fVzN)T^`J=wz}@yyQtlghZ?%xHmFxz?`1D^(}7FQ4o<-R z)V1AKB;6rAg8yTy$Q3g?iW0<*>sZ!e4QKJa2sV=GMWjGxLr8?!o@??ZeyejlQ_@;V09xKR!8I`uR6y zvY=$bjA##iY&}!pb6G$s_{thTZmI))FZ%A^QgPyk`*!^4?*zuOt+8O*loKJB7OW@%r?{~G9;0lIC7NN_6uIhPUZG@oVr@Nj*$mXiB~ z@@#=zSz*F%)+!`!=>xrzv?Mtv`8bv4BJW<~k=u5Lsf;|`Vy~Ee(f%a+{aQ};h+XXV zm>OPjJ0Zq5dCxU4)CnO;d`wWcw8@2;`%1n~P+`BvmGu5f@ih_O|H3Sdtp4#!_42OM zeiMkHF${{Q?2$d+s~#75vw7VInyDksAh=GF-Y`Ld!7#nRR+8vs2+b!ey@LJ}Pn!Hl z#X#`6F*YF4nFA7?RmmhZxrjz=v#)~ z;8Vc?rst;(H`phg`geeV!I_VF{^q$%c8nG#3U+o}#U(|liMd=(AzTUy`fi2_MkWd& rX+XBQ5fB+Dgror38Ugtwi6!}(3L#lst|45Lbyy0S%nT;aXQ>AOiBBk; delta 166 zcmV;X09pTq3Z4p(ECVw!Fp)1B4>&a-FHRsaATLxPF)=kVu~<9-0yH#}mI3S=J_a~| zvY8pc@^HWc=gTp$KVr&lU;qG1Fau+=odP}qQ8*wkMrmwxWpW@dMr>hpWkh9TZ)9a4 zK0XR_baG{3Z3=jta|+>7P|$ZXR4_782uTC7&5eM_Kp`Xr$kqtRFG(!P*Hj3};&Kh) U0szYa3CNQZ1aSg5HIs$}e@96;TL1t6 diff --git a/qpdf/qtest/qpdf/object-stream.generate.exp b/qpdf/qtest/qpdf/object-stream.generate.exp index 87e2f5dacbc13acc24db7adcf87be29bc6f37ac8..3b16f12147d40e8f3e21274115cad2fb7f71fb38 100644 GIT binary patch delta 447 zcmYjNy-vbV6u!5BA(g~M4LBWL#DwIw1fN{{tk(clk2fevp?)m-BJ>N9+CEmwk@l+gG1)7jl6_6DPTEOa{0xN+m ztN7MH2BvvVa(4PiC7z{OA7=O^?MEqDA(VU&Do})icqY`hi+CUc33{LifxiVdXVVQ{ z6((Csn+UJlp&>EiR9-HuKCRzI!tF@`Fw?UaI70><16DP=4vL{-&5vi)9QOS1z;`tp(jN$~Jbhig7V9 zT(hG?k(I2D-8HQ~uoKhTw;b~?P}2uUiOaC#aob(+ds94>7tVqVXYo(JMAN(;I1ds$ O)PF~QWnAL(z}gSV8*qsL delta 593 zcmcc1)xa}BN8i-gOhMm6AzH!A*j&NT$Uq@hLEm2iDCP>}nHehR`zZj45D>>e!Om{- zc}ByD4c-&4D>@k~=(}a+l%y5`>72xpRF~A`{FGD$eFz)WJOl>i4D?|n97t^r7|!bljkX5NHu0OvNf!(Qgc3j0JMX8CoTorSQxgG`uTqrjVF)#|`@MPX} zwzuJggQ5!SiEA7u^gVS>UJv0o`KXBZArr%CM$QwH+nJ5UO@IM|B?Kn#VV0_|nB&@e zn(L5(h|BlCT|b*|JBV^EeV{GEA}AU&MK7sZlhK#U)Aj$oY==9%htF857OjwRWn8*J zWAiGe+&^!WL^AT*8^pYC&biR2_rBB7-MGjhHht}rwbN$B3&!qR=bP~MsW0#S`7*ir z4%XLyAKWEavNi8%(_eA+&jO3Yu2=3m%GdJW>&p455sQ8?9D8CAKe?90z}XNOsaTv# iN`y^b$C51oiYW#rHMeQoc^H_47`_9eLvgYPt0(~I_t5kJ diff --git a/qpdf/qtest/qpdf/object-stream.preserve.exp b/qpdf/qtest/qpdf/object-stream.preserve.exp index 87e2f5dacbc13acc24db7adcf87be29bc6f37ac8..3b16f12147d40e8f3e21274115cad2fb7f71fb38 100644 GIT binary patch delta 447 zcmYjNy-vbV6u!5BA(g~M4LBWL#DwIw1fN{{tk(clk2fevp?)m-BJ>N9+CEmwk@l+gG1)7jl6_6DPTEOa{0xN+m ztN7MH2BvvVa(4PiC7z{OA7=O^?MEqDA(VU&Do})icqY`hi+CUc33{LifxiVdXVVQ{ z6((Csn+UJlp&>EiR9-HuKCRzI!tF@`Fw?UaI70><16DP=4vL{-&5vi)9QOS1z;`tp(jN$~Jbhig7V9 zT(hG?k(I2D-8HQ~uoKhTw;b~?P}2uUiOaC#aob(+ds94>7tVqVXYo(JMAN(;I1ds$ O)PF~QWnAL(z}gSV8*qsL delta 593 zcmcc1)xa}BN8i-gOhMm6AzH!A*j&NT$Uq@hLEm2iDCP>}nHehR`zZj45D>>e!Om{- zc}ByD4c-&4D>@k~=(}a+l%y5`>72xpRF~A`{FGD$eFz)WJOl>i4D?|n97t^r7|!bljkX5NHu0OvNf!(Qgc3j0JMX8CoTorSQxgG`uTqrjVF)#|`@MPX} zwzuJggQ5!SiEA7u^gVS>UJv0o`KXBZArr%CM$QwH+nJ5UO@IM|B?Kn#VV0_|nB&@e zn(L5(h|BlCT|b*|JBV^EeV{GEA}AU&MK7sZlhK#U)Aj$oY==9%htF857OjwRWn8*J zWAiGe+&^!WL^AT*8^pYC&biR2_rBB7-MGjhHht}rwbN$B3&!qR=bP~MsW0#S`7*ir z4%XLyAKWEavNi8%(_eA+&jO3Yu2=3m%GdJW>&p455sQ8?9D8CAKe?90z}XNOsaTv# iN`y^b$C51oiYW#rHMeQoc^H_47`_9eLvgYPt0(~I_t5kJ diff --git a/qpdf/qtest/qpdf/test4-1.qdf b/qpdf/qtest/qpdf/test4-1.qdf index b22fd6f..130cad7 100644 --- a/qpdf/qtest/qpdf/test4-1.qdf +++ b/qpdf/qtest/qpdf/test4-1.qdf @@ -39,8 +39,8 @@ endobj << /A 5 0 R /B 6 0 R - /Subject (Subject) - /Title (Some Title Is Here) + /Subject 7 0 R + /Title 8 0 R >> endobj @@ -49,7 +49,7 @@ endobj << /Count 1 /Kids [ - 7 0 R + 9 0 R ] /Type /Pages >> @@ -72,11 +72,21 @@ endobj >> endobj +%% Original object ID: 10 0 +7 0 obj +(Subject) +endobj + +%% Original object ID: 9 0 +8 0 obj +(Some Title Is Here) +endobj + %% Page 1 %% Original object ID: 3 0 -7 0 obj +9 0 obj << - /Contents 8 0 R + /Contents 10 0 R /MediaBox [ 0 0 @@ -86,9 +96,9 @@ endobj /Parent 4 0 R /Resources << /Font << - /F1 10 0 R + /F1 12 0 R >> - /ProcSet 11 0 R + /ProcSet 13 0 R >> /Type /Page >> @@ -96,9 +106,9 @@ endobj %% Contents for page 1 %% Original object ID: 4 0 -8 0 obj +10 0 obj << - /Length 9 0 R + /Length 11 0 R >> stream BT @@ -109,12 +119,12 @@ ET endstream endobj -9 0 obj +11 0 obj 44 endobj %% Original object ID: 6 0 -10 0 obj +12 0 obj << /BaseFont /Helvetica /Encoding /WinAnsiEncoding @@ -125,7 +135,7 @@ endobj endobj %% Original object ID: 7 0 -11 0 obj +13 0 obj [ /PDF /Text @@ -133,26 +143,28 @@ endobj endobj xref -0 12 +0 14 0000000000 65535 f 0000000052 00000 n 0000000134 00000 n 0000000353 00000 n -0000000475 00000 n -0000000575 00000 n -0000000635 00000 n -0000000714 00000 n -0000000958 00000 n -0000001057 00000 n -0000001103 00000 n -0000001249 00000 n +0000000456 00000 n +0000000556 00000 n +0000000616 00000 n +0000000686 00000 n +0000000739 00000 n +0000000813 00000 n +0000001058 00000 n +0000001159 00000 n +0000001206 00000 n +0000001352 00000 n trailer << /Info 2 0 R /QTest 3 0 R /Root 1 0 R - /Size 12 + /Size 14 /ID [<31415926535897932384626433832795>] >> startxref -1285 +1388 %%EOF diff --git a/qpdf/qtest/qpdf/unfilterable-with-crypt-after.out b/qpdf/qtest/qpdf/unfilterable-with-crypt-after.out new file mode 100644 index 0000000..ac52313 --- /dev/null +++ b/qpdf/qtest/qpdf/unfilterable-with-crypt-after.out @@ -0,0 +1,4 @@ +<< /DL 30 /DecodeParms [ null ] /Filter [ /ZlateDecode ] /Length 39 /Params << /CheckSum /CreationDate (D:20121229172641-05'00') /ModDate (D:20121229172600) /Size 30 >> /Subtype /text#2fplain >>attachment1.txt: +This is the first attachment. +--END-- +test 36 done diff --git a/qpdf/qtest/qpdf/unfilterable-with-crypt-before.out b/qpdf/qtest/qpdf/unfilterable-with-crypt-before.out new file mode 100644 index 0000000..36e2852 --- /dev/null +++ b/qpdf/qtest/qpdf/unfilterable-with-crypt-before.out @@ -0,0 +1,4 @@ +<< /DL 30 /DecodeParms [ << /Name /StdCF >> null ] /Filter [ /Crypt /ZlateDecode ] /Length 64 /Params << /CheckSum /CreationDate (D:20121229172641-05'00') /ModDate (D:20121229172600) /Size 30 >> /Subtype /text#2fplain >>attachment1.txt: +This is the first attachment. +--END-- +test 36 done diff --git a/qpdf/qtest/qpdf/unfilterable-with-crypt.pdf b/qpdf/qtest/qpdf/unfilterable-with-crypt.pdf new file mode 100644 index 0000000000000000000000000000000000000000..970ea536504278e7ff0e71035a1da5317dadeacc GIT binary patch literal 17868 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 W@o3e0BLu;5@Bcs|vA^E{^8WzswHI^% literal 0 HcmV?d00001 diff --git a/qpdf/qtest/qpdf/unreferenced-indirect-scalar.out b/qpdf/qtest/qpdf/unreferenced-indirect-scalar.out index c1bf4a9d1563afc623e61e6911b148d8e8563438..af070310588d52a42788f30c7b3e0ef209bf6330 100644 GIT binary patch delta 56 zcmeyx^_Odc1Ea-6M`uR!$&JiX8|U9+Vzp2(Pzc&w&wP@RlbwN)B^!wACkL`BaGDx& Lsj9mAyKw;k<<|~% delta 59 zcmey%^^0qQ1Ec9gM`uQp$?ut@HqO7t#G)U?6$|2Qp2xhFk&}giktG9&YbJ-VDsY-u MaH*=g`nz!f07a4zasU7T diff --git a/qpdf/qtest/qpdf/zero-offset.out b/qpdf/qtest/qpdf/zero-offset.out new file mode 100644 index 0000000..df99146 --- /dev/null +++ b/qpdf/qtest/qpdf/zero-offset.out @@ -0,0 +1,5 @@ +checking zero-offset.pdf +PDF Version: 1.3 +File is not encrypted +File is not linearized +WARNING: zero-offset.pdf (object 27 0): object has offset 0 diff --git a/qpdf/qtest/qpdf/zero-offset.pdf b/qpdf/qtest/qpdf/zero-offset.pdf new file mode 100644 index 0000000000000000000000000000000000000000..ff59b45796564fe9d988d405bf83d2bfdd1f22c7 GIT binary patch literal 80688 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 g&HqpG*K_Q file_buf; FILE* filep = 0; @@ -95,7 +112,12 @@ void runtest(int n, char const* filename1, char const* filename2) { pdf.setAttemptRecovery(false); } - if (n % 2 == 0) + if (((n == 35) || (n == 36)) && (arg2 != 0)) + { + // arg2 is password + pdf.processFile(filename1, arg2); + } + else if (n % 2 == 0) { if (n % 4 == 0) { @@ -936,9 +958,9 @@ void runtest(int n, char const* filename1, char const* filename2) // Copy qtest without crossing page boundaries. Should get O1 // and O2 and their streams but not O3 or any other pages. - assert(filename2 != 0); + assert(arg2 != 0); QPDF newpdf; - newpdf.processFile(filename2); + newpdf.processFile(arg2); QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); newpdf.getTrailer().replaceKey( "/QTest", newpdf.copyForeignObject(qtest)); @@ -956,9 +978,9 @@ void runtest(int n, char const* filename1, char const* filename2) // that O3 points to. Also, inherited object will have been // pushed down and will be preserved. - assert(filename2 != 0); + assert(arg2 != 0); QPDF newpdf; - newpdf.processFile(filename2); + newpdf.processFile(arg2); QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); QPDFObjectHandle O3 = qtest.getKey("/O3"); newpdf.addPage(O3, false); @@ -976,9 +998,9 @@ void runtest(int n, char const* filename1, char const* filename2) // Should get qtest plus only the O3 page and the page that O3 // points to. Inherited objects should be preserved. - assert(filename2 != 0); + assert(arg2 != 0); QPDF newpdf; - newpdf.processFile(filename2); + newpdf.processFile(arg2); QPDFObjectHandle qtest = pdf.getTrailer().getKey("/QTest"); QPDFObjectHandle O3 = qtest.getKey("/O3"); newpdf.addPage(O3.getKey("/OtherPage"), false); @@ -1016,9 +1038,9 @@ void runtest(int n, char const* filename1, char const* filename2) else if (n == 29) { // Detect mixed objects in QPDFWriter - assert(filename2 != 0); + assert(arg2 != 0); QPDF other; - other.processFile(filename2); + other.processFile(arg2); // Should use copyForeignObject instead other.getTrailer().replaceKey( "/QTest", pdf.getTrailer().getKey("/QTest")); @@ -1036,9 +1058,9 @@ void runtest(int n, char const* filename1, char const* filename2) } else if (n == 30) { - assert(filename2 != 0); + assert(arg2 != 0); QPDF encrypted; - encrypted.processFile(filename2, "user"); + encrypted.processFile(arg2, "user"); QPDFWriter w(pdf, "b.pdf"); w.setStreamDataMode(qpdf_s_preserve); w.copyEncryptionParameters(encrypted); @@ -1112,6 +1134,117 @@ void runtest(int n, char const* filename1, char const* filename2) w.write(); } } + else if (n == 33) + { + // Test writing to a custom pipeline + Pl_Buffer p("buffer"); + QPDFWriter w(pdf); + w.setStaticID(true); + w.setOutputPipeline(&p); + w.write(); + PointerHolder b = p.getBuffer(); + FILE* f = QUtil::fopen_wrapper("open a.pdf", + fopen("a.pdf", "wb")); + fwrite(b->getBuffer(), b->getSize(), 1, f); + fclose(f); + } + else if (n == 34) + { + // Look at Extensions dictionary + std::cout << "version: " << pdf.getPDFVersion() << std::endl + << "extension level: " << pdf.getExtensionLevel() << std::endl + << pdf.getRoot().getKey("/Extensions").unparse() << std::endl; + } + else if (n == 35) + { + // Extract attachments + + std::map > attachments; + QPDFObjectHandle root = pdf.getRoot(); + QPDFObjectHandle names = root.getKey("/Names"); + QPDFObjectHandle embeddedFiles = names.getKey("/EmbeddedFiles"); + names = embeddedFiles.getKey("/Names"); + for (int i = 0; i < names.getArrayNItems(); ++i) + { + QPDFObjectHandle item = names.getArrayItem(i); + if (item.isDictionary() && + item.getKey("/Type").isName() && + (item.getKey("/Type").getName() == "/Filespec") && + item.getKey("/EF").isDictionary() && + item.getKey("/EF").getKey("/F").isStream()) + { + std::string filename = item.getKey("/F").getStringValue(); + QPDFObjectHandle stream = item.getKey("/EF").getKey("/F"); + attachments[filename] = stream.getStreamData(); + } + } + for (std::map >::iterator iter = + attachments.begin(); iter != attachments.end(); ++iter) + { + std::string const& filename = (*iter).first; + std::string data = std::string( + (char const*)(*iter).second->getBuffer(), + (*iter).second->getSize()); + bool is_binary = false; + for (size_t i = 0; i < data.size(); ++i) + { + if (data[i] < 0) + { + is_binary = true; + break; + } + } + if (is_binary) + { + std::string t; + for (size_t i = 0; i < std::min(data.size(), (size_t)20); ++i) + { + if ((data[i] >= 32) && (data[i] <= 126)) + { + t += data[i]; + } + else + { + t += "."; + } + } + t += " (" + QUtil::int_to_string(data.size()) + " bytes)"; + data = t; + } + std::cout << filename << ":\n" << data << "--END--\n"; + } + } + else if (n == 36) + { + // Extract raw unfilterable attachment + + QPDFObjectHandle root = pdf.getRoot(); + QPDFObjectHandle names = root.getKey("/Names"); + QPDFObjectHandle embeddedFiles = names.getKey("/EmbeddedFiles"); + names = embeddedFiles.getKey("/Names"); + for (int i = 0; i < names.getArrayNItems(); ++i) + { + QPDFObjectHandle item = names.getArrayItem(i); + if (item.isDictionary() && + item.getKey("/Type").isName() && + (item.getKey("/Type").getName() == "/Filespec") && + item.getKey("/EF").isDictionary() && + item.getKey("/EF").getKey("/F").isStream() && + (item.getKey("/F").getStringValue() == "attachment1.txt")) + { + std::string filename = item.getKey("/F").getStringValue(); + QPDFObjectHandle stream = item.getKey("/EF").getKey("/F"); + Pl_Buffer p1("buffer"); + Pl_Flate p2("compress", &p1, Pl_Flate::a_inflate); + stream.pipeStreamData(&p2, false, false, false); + PointerHolder buf = p1.getBuffer(); + std::string data = std::string( + (char const*)buf->getBuffer(), buf->getSize()); + std::cout << stream.getDict().unparse() + << filename << ":\n" << data << "--END--\n"; + } + } + } else { throw std::runtime_error(std::string("invalid test ") + @@ -1151,8 +1284,8 @@ int main(int argc, char* argv[]) { int n = atoi(argv[1]); char const* filename1 = argv[2]; - char const* filename2 = argv[3]; - runtest(n, filename1, filename2); + char const* arg2 = argv[3]; + runtest(n, filename1, arg2); } catch (std::exception& e) { -- 2.7.4