Commit 846c9f6bcc9aa86067850088808ff8d724a0d18f

Authored by Jay Berkenbilt
1 parent ad19b03f

checkpoint -- started doing some R4 encryption support

git-svn-id: svn+q:///qpdf/trunk@807 71b93d88-0707-0410-a8cf-f5a4172ac649
... ... @@ -8,6 +8,32 @@ Artistic License which may be found in the source distribution as
8 8 "Artistic-2.0". It is provided "as is" without express or implied
9 9 warranty.
10 10  
  11 +
  12 +Licensing terms of embedded software
  13 +====================================
  14 +
  15 +Some additional software with additional licensing terms is embedded
  16 +within the qpdf source distribution in "external-libs". This software
  17 +is not actually used by the qpdf build unless the
  18 +--enable-build-external-libs option is passed to ./configure. These
  19 +packages have their own licensing terms, both of which are compatible
  20 +with qpdf's license.
  21 +
  22 +Zlib's license can be read in external-libs/zlib/zlib.h
  23 +
  24 +PCRE's licensing terms can be found in external-libs/pcre/LICENSE.
  25 +PCRE's licensing terms require that we include the following
  26 +information:
  27 +
  28 + Regular expression support is provided by the PCRE library package,
  29 + which is open source software, written by Philip Hazel, and
  30 + copyright by the University of Cambridge, England.
  31 +
  32 +The sources to PCRE can be independently obtained from
  33 +
  34 + ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/
  35 +
  36 +
11 37 Building on UNIX/Linux
12 38 ======================
13 39  
... ... @@ -24,11 +50,11 @@ Building on Windows
24 50 ===================
25 51  
26 52 QPDF is known to build and pass its test suite with mingw (gcc 4.4.0)
27   -and Microsoft Visual C++ .NET 2008 Express. In both cases, cygwin is
28   -required to run the test suite. Either cygwin or MSYS is required to
29   -build as well in order to get make and other related tools. The MSVC
30   -build has only been tested under cygwin. The mingw build requires
31   -MSYS and will probably not work with cygwin.
  53 +and Microsoft Visual C++ .NET 2008 Express. Either cygwin or MSYS
  54 +plus ActivateState Perl is required to build as well in order to get
  55 +make and other related tools. The MSVC works with either cygwin or
  56 +MSYS. The mingw build requires MSYS and will probably not work with
  57 +cygwin.
32 58  
33 59 For details on how to build under Windows, see README.windows.
34 60  
... ...
... ... @@ -56,6 +56,18 @@
56 56 dictionary. (V = 4 is Crypt Filters.) See
57 57 ~/Q/pdf-collection/R4-encrypt-PDF_Inside_and_Out.pdf
58 58  
  59 + Search for XXX in the code. Implementation has been started.
  60 +
  61 + Algorithms from PDF Spec in QPDF_encrypt.cc have been updated. We
  62 + can at least properly verify the user password with an R4 file. In
  63 + order to finish the job, we need an aes-128-cbc implementation.
  64 + Then we can fill in the gaps for the aes pipeline and actually run
  65 + the test suite. The pipeline may be able to hard-code the
  66 + initialization vector stuff by taking the first block of input and
  67 + by writing a random block for output. The padding is already in
  68 + the code, but the initialization vector is not since I accidentally
  69 + started using an aes256 implementation instead of aes128-cbc.
  70 +
59 71 * Look at page splitting.
60 72  
61 73  
... ...
include/qpdf/QPDF.hh
... ... @@ -92,14 +92,15 @@ class DLL_EXPORT QPDF
92 92 // This class holds data read from the encryption dictionary.
93 93 EncryptionData(int V, int R, int Length_bytes, int P,
94 94 std::string const& O, std::string const& U,
95   - std::string const& id1) :
  95 + std::string const& id1, bool encrypt_metadata) :
96 96 V(V),
97 97 R(R),
98 98 Length_bytes(Length_bytes),
99 99 P(P),
100 100 O(O),
101 101 U(U),
102   - id1(id1)
  102 + id1(id1),
  103 + encrypt_metadata(encrypt_metadata)
103 104 {
104 105 }
105 106  
... ... @@ -110,6 +111,7 @@ class DLL_EXPORT QPDF
110 111 std::string O;
111 112 std::string U;
112 113 std::string id1;
  114 + bool encrypt_metadata;
113 115 };
114 116  
115 117 bool isEncrypted() const;
... ... @@ -132,7 +134,8 @@ class DLL_EXPORT QPDF
132 134 // getTrimmedUserPassword's result.
133 135 static void trim_user_password(std::string& user_password);
134 136 static std::string compute_data_key(
135   - std::string const& encryption_key, int objid, int generation);
  137 + std::string const& encryption_key, int objid, int generation,
  138 + bool use_aes);
136 139 static std::string compute_encryption_key(
137 140 std::string const& password, EncryptionData const& data);
138 141  
... ... @@ -732,6 +735,7 @@ class DLL_EXPORT QPDF
732 735 bool ignore_xref_streams;
733 736 bool suppress_warnings;
734 737 bool attempt_recovery;
  738 + bool encryption_use_aes;
735 739 std::string provided_password;
736 740 std::string user_password;
737 741 std::string encryption_key;
... ...
libqpdf/Pl_AES_PDF.cc 0 → 100644
  1 +#include <qpdf/Pl_AES_PDF.hh>
  2 +#include <qpdf/QUtil.hh>
  3 +#include <cstring>
  4 +#include <assert.h>
  5 +#include <stdexcept>
  6 +
  7 +Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next,
  8 + bool encrypt, unsigned char* key_data) :
  9 + Pipeline(identifier, next),
  10 + encrypt(encrypt),
  11 + offset(0)
  12 +{
  13 + std::memset(this->buf, 0, this->buf_size);
  14 + // XXX init
  15 +}
  16 +
  17 +Pl_AES_PDF::~Pl_AES_PDF()
  18 +{
  19 + // XXX finalize
  20 +}
  21 +
  22 +void
  23 +Pl_AES_PDF::write(unsigned char* data, int len)
  24 +{
  25 + unsigned int bytes_left = len;
  26 + unsigned char* p = data;
  27 +
  28 + while (bytes_left > 0)
  29 + {
  30 + if (this->offset == this->buf_size)
  31 + {
  32 + flush(false);
  33 + }
  34 +
  35 + unsigned int available = this->buf_size - this->offset;
  36 + int bytes = (bytes_left < available ? bytes_left : available);
  37 + bytes_left -= bytes;
  38 + std::memcpy(this->buf + this->offset, p, bytes);
  39 + this->offset += bytes;
  40 + p += bytes;
  41 + }
  42 +}
  43 +
  44 +void
  45 +Pl_AES_PDF::finish()
  46 +{
  47 + if (this->encrypt)
  48 + {
  49 + if (this->offset == this->buf_size)
  50 + {
  51 + flush(false);
  52 + }
  53 + // Pad as described in section 3.5.1 of version 1.7 of the PDF
  54 + // specification, including providing an entire block of padding
  55 + // if the input was a multiple of 16 bytes.
  56 + unsigned char pad = this->buf_size - this->offset;
  57 + memset(this->buf + this->offset, pad, pad);
  58 + this->offset = this->buf_size;
  59 + flush(false);
  60 + }
  61 + else
  62 + {
  63 + if (this->offset != this->buf_size)
  64 + {
  65 + throw std::runtime_error(
  66 + "aes encrypted stream length was not a multiple of " +
  67 + QUtil::int_to_string(this->buf_size) + " bytes (offset = " +
  68 + QUtil::int_to_string(this->offset) + ")");
  69 + }
  70 + flush(true);
  71 + }
  72 + getNext()->finish();
  73 +}
  74 +
  75 +void
  76 +Pl_AES_PDF::flush(bool strip_padding)
  77 +{
  78 + assert(this->offset == this->buf_size);
  79 + if (this->encrypt)
  80 + {
  81 + // XXX encrypt this->buf
  82 + }
  83 + else
  84 + {
  85 + // XXX decrypt this->buf
  86 + }
  87 + unsigned int bytes = this->buf_size;
  88 + if (strip_padding)
  89 + {
  90 + unsigned char last = this->buf[this->buf_size - 1];
  91 + if (last <= this->buf_size)
  92 + {
  93 + bool strip = true;
  94 + for (unsigned int i = 1; i <= last; ++i)
  95 + {
  96 + if (this->buf[this->buf_size - i] != last)
  97 + {
  98 + strip = false;
  99 + break;
  100 + }
  101 + }
  102 + if (strip)
  103 + {
  104 + bytes -= last;
  105 + }
  106 + }
  107 + }
  108 + getNext()->write(this->buf, bytes);
  109 + this->offset = 0;
  110 +}
... ...
libqpdf/QPDF.cc
... ... @@ -253,6 +253,7 @@ QPDF::QPDF() :
253 253 ignore_xref_streams(false),
254 254 suppress_warnings(false),
255 255 attempt_recovery(true),
  256 + encryption_use_aes(false),
256 257 cached_key_objid(0),
257 258 cached_key_generation(0),
258 259 first_xref_item_offset(0),
... ...
libqpdf/QPDFWriter.cc
... ... @@ -326,7 +326,8 @@ QPDFWriter::setEncryptionParametersInternal(
326 326 encryption_dictionary["/O"] = QPDF_String(O).unparse(true);
327 327 encryption_dictionary["/U"] = QPDF_String(U).unparse(true);
328 328 this->encrypted = true;
329   - QPDF::EncryptionData encryption_data(V, R, key_len, P, O, U, this->id1);
  329 + QPDF::EncryptionData encryption_data(V, R, key_len, P, O, U, this->id1,
  330 + /*XXX encrypt_metadata*/true);
330 331 this->encryption_key = QPDF::compute_encryption_key(
331 332 user_password, encryption_data);
332 333 }
... ... @@ -335,7 +336,7 @@ void
335 336 QPDFWriter::setDataKey(int objid)
336 337 {
337 338 this->cur_data_key = QPDF::compute_data_key(
338   - this->encryption_key, objid, 0);
  339 + this->encryption_key, objid, 0, /*XXX use_aes */false);
339 340 }
340 341  
341 342 int
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -99,9 +99,10 @@ iterate_rc4(unsigned char* data, int data_len,
99 99  
100 100 std::string
101 101 QPDF::compute_data_key(std::string const& encryption_key,
102   - int objid, int generation)
  102 + int objid, int generation,
  103 + bool use_aes)
103 104 {
104   - // Algorithm 3.1 from the PDF 1.4 Reference Manual
  105 + // Algorithm 3.1 from the PDF 1.7 Reference Manual
105 106  
106 107 std::string result = encryption_key;
107 108  
... ... @@ -111,6 +112,10 @@ QPDF::compute_data_key(std::string const&amp; encryption_key,
111 112 result += (char) ((objid >> 16) & 0xff);
112 113 result += (char) (generation & 0xff);
113 114 result += (char) ((generation >> 8) & 0xff);
  115 + if (use_aes)
  116 + {
  117 + result += "sAlT";
  118 + }
114 119  
115 120 MD5 md5;
116 121 md5.encodeDataIncrementally(result.c_str(), result.length());
... ... @@ -118,13 +123,16 @@ QPDF::compute_data_key(std::string const&amp; encryption_key,
118 123 md5.digest(digest);
119 124 return std::string((char*) digest,
120 125 std::min(result.length(), (size_t) 16));
  126 +
  127 + // XXX Item 4 in Algorithm 3.1 mentions CBC and a random number.
  128 + // We still have to incorporate that.
121 129 }
122 130  
123 131 std::string
124 132 QPDF::compute_encryption_key(
125 133 std::string const& password, EncryptionData const& data)
126 134 {
127   - // Algorithm 3.2 from the PDF 1.4 Reference Manual
  135 + // Algorithm 3.2 from the PDF 1.7 Reference Manual
128 136  
129 137 MD5 md5;
130 138 md5.encodeDataIncrementally(
... ... @@ -137,8 +145,14 @@ QPDF::compute_encryption_key(
137 145 pbytes[3] = (char) ((data.P >> 24) & 0xff);
138 146 md5.encodeDataIncrementally(pbytes, 4);
139 147 md5.encodeDataIncrementally(data.id1.c_str(), id_bytes);
  148 + if ((data.R >= 4) && (! data.encrypt_metadata))
  149 + {
  150 + char bytes[4];
  151 + memset(bytes, 0xff, 4);
  152 + md5.encodeDataIncrementally(bytes, 4);
  153 + }
140 154 MD5::Digest digest;
141   - iterate_md5_digest(md5, digest, ((data.R == 3) ? 50 : 0));
  155 + iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0));
142 156 return std::string((char*)digest, data.Length_bytes);
143 157 }
144 158  
... ... @@ -157,7 +171,7 @@ compute_O_rc4_key(std::string const&amp; user_password,
157 171 md5.encodeDataIncrementally(
158 172 pad_or_truncate_password(password).c_str(), key_bytes);
159 173 MD5::Digest digest;
160   - iterate_md5_digest(md5, digest, ((data.R == 3) ? 50 : 0));
  174 + iterate_md5_digest(md5, digest, ((data.R >= 3) ? 50 : 0));
161 175 memcpy(key, digest, O_key_bytes);
162 176 }
163 177  
... ... @@ -166,7 +180,7 @@ compute_O_value(std::string const&amp; user_password,
166 180 std::string const& owner_password,
167 181 QPDF::EncryptionData const& data)
168 182 {
169   - // Algorithm 3.3 from the PDF 1.4 Reference Manual
  183 + // Algorithm 3.3 from the PDF 1.7 Reference Manual
170 184  
171 185 unsigned char O_key[O_key_bytes];
172 186 compute_O_rc4_key(user_password, owner_password, data, O_key);
... ... @@ -174,7 +188,7 @@ compute_O_value(std::string const&amp; user_password,
174 188 char upass[key_bytes];
175 189 pad_or_truncate_password(user_password, upass);
176 190 iterate_rc4((unsigned char*) upass, key_bytes,
177   - O_key, data.Length_bytes, (data.R == 3) ? 20 : 1, false);
  191 + O_key, data.Length_bytes, (data.R >= 3) ? 20 : 1, false);
178 192 return std::string(upass, key_bytes);
179 193 }
180 194  
... ... @@ -183,7 +197,7 @@ std::string
183 197 compute_U_value_R2(std::string const& user_password,
184 198 QPDF::EncryptionData const& data)
185 199 {
186   - // Algorithm 3.4 from the PDF 1.4 Reference Manual
  200 + // Algorithm 3.4 from the PDF 1.7 Reference Manual
187 201  
188 202 std::string k1 = QPDF::compute_encryption_key(user_password, data);
189 203 char udata[key_bytes];
... ... @@ -198,7 +212,7 @@ std::string
198 212 compute_U_value_R3(std::string const& user_password,
199 213 QPDF::EncryptionData const& data)
200 214 {
201   - // Algorithm 3.5 from the PDF 1.4 Reference Manual
  215 + // Algorithm 3.5 from the PDF 1.7 Reference Manual
202 216  
203 217 std::string k1 = QPDF::compute_encryption_key(user_password, data);
204 218 MD5 md5;
... ... @@ -224,7 +238,7 @@ static std::string
224 238 compute_U_value(std::string const& user_password,
225 239 QPDF::EncryptionData const& data)
226 240 {
227   - if (data.R == 3)
  241 + if (data.R >= 3)
228 242 {
229 243 return compute_U_value_R3(user_password, data);
230 244 }
... ... @@ -236,10 +250,10 @@ static bool
236 250 check_user_password(std::string const& user_password,
237 251 QPDF::EncryptionData const& data)
238 252 {
239   - // Algorithm 3.6 from the PDF 1.4 Reference Manual
  253 + // Algorithm 3.6 from the PDF 1.7 Reference Manual
240 254  
241 255 std::string u_value = compute_U_value(user_password, data);
242   - int to_compare = ((data.R == 3) ? sizeof(MD5::Digest) : key_bytes);
  256 + int to_compare = ((data.R >= 3) ? sizeof(MD5::Digest) : key_bytes);
243 257 return (memcmp(data.U.c_str(), u_value.c_str(), to_compare) == 0);
244 258 }
245 259  
... ... @@ -248,14 +262,14 @@ check_owner_password(std::string&amp; user_password,
248 262 std::string const& owner_password,
249 263 QPDF::EncryptionData const& data)
250 264 {
251   - // Algorithm 3.7 from the PDF 1.4 Reference Manual
  265 + // Algorithm 3.7 from the PDF 1.7 Reference Manual
252 266  
253 267 unsigned char key[O_key_bytes];
254 268 compute_O_rc4_key(user_password, owner_password, data, key);
255 269 unsigned char O_data[key_bytes];
256 270 memcpy(O_data, (unsigned char*) data.O.c_str(), key_bytes);
257 271 iterate_rc4(O_data, key_bytes, key, data.Length_bytes,
258   - (data.R == 3) ? 20 : 1, true);
  272 + (data.R >= 3) ? 20 : 1, true);
259 273 std::string new_user_password =
260 274 std::string((char*)O_data, key_bytes);
261 275 bool result = false;
... ... @@ -339,13 +353,20 @@ QPDF::initializeEncryption()
339 353 std::string U = encryption_dict.getKey("/U").getStringValue();
340 354 unsigned int P = (unsigned int) encryption_dict.getKey("/P").getIntValue();
341 355  
342   - if (! (((R == 2) || (R == 3)) &&
343   - ((V == 1) || (V == 2))))
  356 + if (! (((R == 2) || (R == 3) || (R == 4)) &&
  357 + ((V == 1) || (V == 2) || (V == 4))))
344 358 {
345 359 throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
346 360 "Unsupported /R or /V in encryption dictionary");
347 361 }
348 362  
  363 + // XXX remove this check to continue implementing R4.
  364 + if ((R == 4) || (V == 4))
  365 + {
  366 + throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
  367 + "PDF >= 1.5 encryption support is not fully implemented");
  368 + }
  369 +
349 370 if (! ((O.length() == key_bytes) && (U.length() == key_bytes)))
350 371 {
351 372 throw QPDFExc(this->file.getName(), this->file.getLastOffset(),
... ... @@ -364,7 +385,18 @@ QPDF::initializeEncryption()
364 385 }
365 386 }
366 387  
367   - EncryptionData data(V, R, Length / 8, P, O, U, id1);
  388 + bool encrypt_metadata = true;
  389 + if ((V >= 4) && (encryption_dict.getKey("/EncryptMetadata").isBool()))
  390 + {
  391 + encrypt_metadata =
  392 + encryption_dict.getKey("/EncryptMetadata").getBoolValue();
  393 + }
  394 + // XXX not really...
  395 + if (R >= 4)
  396 + {
  397 + this->encryption_use_aes = true;
  398 + }
  399 + EncryptionData data(V, R, Length / 8, P, O, U, id1, encrypt_metadata);
368 400 if (check_owner_password(this->user_password, this->provided_password, data))
369 401 {
370 402 // password supplied was owner password; user_password has
... ... @@ -395,7 +427,8 @@ QPDF::getKeyForObject(int objid, int generation)
395 427 (generation == this->cached_key_generation)))
396 428 {
397 429 this->cached_object_encryption_key =
398   - compute_data_key(this->encryption_key, objid, generation);
  430 + compute_data_key(this->encryption_key, objid, generation,
  431 + this->encryption_use_aes);
399 432 this->cached_key_objid = objid;
400 433 this->cached_key_generation = generation;
401 434 }
... ... @@ -424,8 +457,15 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
424 457 std::vector<PointerHolder<Pipeline> >& heap)
425 458 {
426 459 std::string key = getKeyForObject(objid, generation);
427   - pipeline = new Pl_RC4("stream decryption", pipeline,
428   - (unsigned char*) key.c_str(), key.length());
  460 + if (this->encryption_use_aes)
  461 + {
  462 + throw std::logic_error("aes not yet implemented"); // XXX
  463 + }
  464 + else
  465 + {
  466 + pipeline = new Pl_RC4("RC4 stream decryption", pipeline,
  467 + (unsigned char*) key.c_str(), key.length());
  468 + }
429 469 heap.push_back(pipeline);
430 470 }
431 471  
... ... @@ -435,7 +475,8 @@ QPDF::compute_encryption_O_U(
435 475 int V, int R, int key_len, int P,
436 476 std::string const& id1, std::string& O, std::string& U)
437 477 {
438   - EncryptionData data(V, R, key_len, P, "", "", id1);
  478 + EncryptionData data(V, R, key_len, P, "", "", id1,
  479 + /*XXX encrypt_metadata*/true);
439 480 data.O = compute_O_value(user_password, owner_password, data);
440 481 O = data.O;
441 482 U = compute_U_value(user_password, data);
... ...
libqpdf/build.mk
... ... @@ -13,6 +13,7 @@ SRCS_libqpdf = \
13 13 libqpdf/MD5.cc \
14 14 libqpdf/PCRE.cc \
15 15 libqpdf/Pipeline.cc \
  16 + libqpdf/Pl_AES_PDF.cc \
16 17 libqpdf/Pl_ASCII85Decoder.cc \
17 18 libqpdf/Pl_ASCIIHexDecoder.cc \
18 19 libqpdf/Pl_Buffer.cc \
... ...
libqpdf/qpdf/Pl_AES_PDF.hh 0 → 100644
  1 +#ifndef __PL_AES_PDF_HH__
  2 +#define __PL_AES_PDF_HH__
  3 +
  4 +#include <qpdf/Pipeline.hh>
  5 +
  6 +class DLL_EXPORT Pl_AES_PDF: public Pipeline
  7 +{
  8 + public:
  9 + // key_data should be a pointer to key_size bytes of data
  10 + static unsigned int const key_size = 16;
  11 + Pl_AES_PDF(char const* identifier, Pipeline* next,
  12 + bool encrypt, unsigned char* key_data);
  13 + virtual ~Pl_AES_PDF();
  14 +
  15 + virtual void write(unsigned char* data, int len);
  16 + virtual void finish();
  17 +
  18 + private:
  19 + void flush(bool discard_padding);
  20 +
  21 + bool encrypt;
  22 + unsigned int offset;
  23 + static unsigned int const buf_size = 16;
  24 + unsigned char buf[buf_size];
  25 +};
  26 +
  27 +#endif // __PL_AES_PDF_HH__
... ...
libtests/aes.cc 0 → 100644
  1 +#include <qpdf/Pl_AES_PDF.hh>
  2 +#include <qpdf/Pl_StdioFile.hh>
  3 +
  4 +#include <stdio.h>
  5 +#include <string.h>
  6 +#include <iostream>
  7 +#include <stdlib.h>
  8 +
  9 +static void usage()
  10 +{
  11 + std::cerr << "Usage: aes { -encrypt | -decrypt }"
  12 + << " hex-key infile outfile" << std::endl;
  13 + exit(2);
  14 +}
  15 +
  16 +int main(int argc, char* argv[])
  17 +{
  18 + if (argc != 5)
  19 + {
  20 + usage();
  21 + }
  22 +
  23 + char* action = argv[1];
  24 + char* hexkey = argv[2];
  25 + char* infilename = argv[3];
  26 + char* outfilename = argv[4];
  27 +
  28 + bool encrypt = true;
  29 + if (strcmp(action, "-decrypt") == 0)
  30 + {
  31 + encrypt = false;
  32 + }
  33 + else if (strcmp(action, "-encrypt") != 0)
  34 + {
  35 + usage();
  36 + }
  37 +
  38 + unsigned int hexkeylen = strlen(hexkey);
  39 + unsigned int keylen = hexkeylen / 2;
  40 + if (keylen != Pl_AES_PDF::key_size)
  41 + {
  42 + std::cerr << "key length must be " << Pl_AES_PDF::key_size
  43 + << " bytes" << std::endl;
  44 + exit(2);
  45 + }
  46 +
  47 + FILE* infile = fopen(infilename, "rb");
  48 + if (infile == 0)
  49 + {
  50 + std::cerr << "can't open " << infilename << std::endl;
  51 + exit(2);
  52 + }
  53 +
  54 + FILE* outfile = fopen(outfilename, "wb");
  55 + if (outfile == 0)
  56 + {
  57 + std::cerr << "can't open " << outfilename << std::endl;
  58 + exit(2);
  59 + }
  60 +
  61 + unsigned char key[Pl_AES_PDF::key_size];
  62 + for (unsigned int i = 0; i < strlen(hexkey); i += 2)
  63 + {
  64 + char t[3];
  65 + t[0] = hexkey[i];
  66 + t[1] = hexkey[i + 1];
  67 + t[2] = '\0';
  68 +
  69 + long val = strtol(t, 0, 16);
  70 + key[i/2] = (unsigned char) val;
  71 + }
  72 +
  73 + Pl_StdioFile* out = new Pl_StdioFile("stdout", outfile);
  74 + Pl_AES_PDF* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, key);
  75 +
  76 + // 16 < buffer size, buffer_size is not a multiple of 8 for testing
  77 + unsigned char buf[83];
  78 + bool done = false;
  79 + while (! done)
  80 + {
  81 + int len = fread(buf, 1, sizeof(buf), infile);
  82 + if (len <= 0)
  83 + {
  84 + done = true;
  85 + }
  86 + else
  87 + {
  88 + aes->write(buf, len);
  89 + }
  90 + }
  91 + aes->finish();
  92 + delete aes;
  93 + delete out;
  94 + fclose(infile);
  95 + fclose(outfile);
  96 + return 0;
  97 +}
... ...
libtests/build.mk
1 1 BINS_libtests = \
  2 + aes \
2 3 ascii85 \
3 4 bits \
4 5 buffer \
... ...