Commit 9b42f526dffd5e1518ff3d83564eb09111ac5f0d

Authored by Jay Berkenbilt
1 parent 77458416

Update AES classes to work with 256-bit keys

libqpdf/Pl_AES_PDF.cc
... ... @@ -15,19 +15,24 @@
15 15 bool Pl_AES_PDF::use_static_iv = false;
16 16  
17 17 Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next,
18   - bool encrypt, unsigned char const key[key_size]) :
  18 + bool encrypt, unsigned char const* key,
  19 + unsigned int key_bytes) :
19 20 Pipeline(identifier, next),
20 21 encrypt(encrypt),
21 22 cbc_mode(true),
22 23 first(true),
23 24 offset(0),
24   - nrounds(0)
  25 + nrounds(0),
  26 + use_zero_iv(false),
  27 + disable_padding(false)
25 28 {
26   - static int const keybits = 128;
27   - assert(key_size == KEYLENGTH(keybits));
28   - assert(sizeof(this->rk) / sizeof(uint32_t) == RKLENGTH(keybits));
29   - std::memcpy(this->key, key, key_size);
30   - std::memset(this->rk, 0, sizeof(this->rk));
  29 + unsigned int keybits = 8 * key_bytes;
  30 + assert(key_bytes == KEYLENGTH(keybits));
  31 + this->key = new unsigned char[key_bytes];
  32 + this->rk = new uint32_t[RKLENGTH(keybits)];
  33 + unsigned int rk_bytes = RKLENGTH(keybits) * sizeof(uint32_t);
  34 + std::memcpy(this->key, key, key_bytes);
  35 + std::memset(this->rk, 0, rk_bytes);
31 36 std::memset(this->inbuf, 0, this->buf_size);
32 37 std::memset(this->outbuf, 0, this->buf_size);
33 38 std::memset(this->cbc_block, 0, this->buf_size);
... ... @@ -44,7 +49,20 @@ Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next,
44 49  
45 50 Pl_AES_PDF::~Pl_AES_PDF()
46 51 {
47   - // nothing needed
  52 + delete [] this->key;
  53 + delete [] this->rk;
  54 +}
  55 +
  56 +void
  57 +Pl_AES_PDF::useZeroIV()
  58 +{
  59 + this->use_zero_iv = true;
  60 +}
  61 +
  62 +void
  63 +Pl_AES_PDF::disablePadding()
  64 +{
  65 + this->disable_padding = true;
48 66 }
49 67  
50 68 void
... ... @@ -90,13 +108,16 @@ Pl_AES_PDF::finish()
90 108 {
91 109 flush(false);
92 110 }
93   - // Pad as described in section 3.5.1 of version 1.7 of the PDF
94   - // specification, including providing an entire block of padding
95   - // if the input was a multiple of 16 bytes.
96   - unsigned char pad = (unsigned char) (this->buf_size - this->offset);
97   - memset(this->inbuf + this->offset, pad, pad);
98   - this->offset = this->buf_size;
99   - flush(false);
  111 + if (! this->disable_padding)
  112 + {
  113 + // Pad as described in section 3.5.1 of version 1.7 of the PDF
  114 + // specification, including providing an entire block of padding
  115 + // if the input was a multiple of 16 bytes.
  116 + unsigned char pad = (unsigned char) (this->buf_size - this->offset);
  117 + memset(this->inbuf + this->offset, pad, pad);
  118 + this->offset = this->buf_size;
  119 + flush(false);
  120 + }
100 121 }
101 122 else
102 123 {
... ... @@ -112,7 +133,7 @@ Pl_AES_PDF::finish()
112 133 this->buf_size - this->offset);
113 134 this->offset = this->buf_size;
114 135 }
115   - flush(true);
  136 + flush(! this->disable_padding);
116 137 }
117 138 getNext()->finish();
118 139 }
... ... @@ -136,6 +157,13 @@ Pl_AES_PDF::initializeVector()
136 157 this->cbc_block[i] = 14 * (1 + i);
137 158 }
138 159 }
  160 + else if (use_zero_iv)
  161 + {
  162 + for (unsigned int i = 0; i < this->buf_size; ++i)
  163 + {
  164 + this->cbc_block[i] = 0;
  165 + }
  166 + }
139 167 else
140 168 {
141 169 for (unsigned int i = 0; i < this->buf_size; ++i)
... ... @@ -157,12 +185,21 @@ Pl_AES_PDF::flush(bool strip_padding)
157 185 {
158 186 if (encrypt)
159 187 {
160   - // Set cbc_block to a random initialization vector and
161   - // write it to the output stream
  188 + // Set cbc_block to the initialization vector, and if
  189 + // not zero, write it to the output stream.
162 190 initializeVector();
163   - getNext()->write(this->cbc_block, this->buf_size);
  191 + if (! this->use_zero_iv)
  192 + {
  193 + getNext()->write(this->cbc_block, this->buf_size);
  194 + }
164 195 }
165   - else
  196 + else if (this->use_zero_iv)
  197 + {
  198 + // Initialize vector with zeroes; zero vector was not
  199 + // written to the beginning of the input file.
  200 + initializeVector();
  201 + }
  202 + else
166 203 {
167 204 // Take the first block of input as the initialization
168 205 // vector. There's nothing to write at this time.
... ...
libqpdf/QPDFWriter.cc
... ... @@ -815,13 +815,14 @@ QPDFWriter::pushEncryptionFilter()
815 815 {
816 816 p = new Pl_AES_PDF(
817 817 "aes stream encryption", this->pipeline, true,
818   - (unsigned char*) this->cur_data_key.c_str());
  818 + (unsigned char*) this->cur_data_key.c_str(),
  819 + (unsigned int)this->cur_data_key.length());
819 820 }
820 821 else
821 822 {
822 823 p = new Pl_RC4("rc4 stream encryption", this->pipeline,
823 824 (unsigned char*) this->cur_data_key.c_str(),
824   - (int)this->cur_data_key.length());
  825 + (unsigned int)this->cur_data_key.length());
825 826 }
826 827 pushPipeline(p);
827 828 }
... ... @@ -1415,7 +1416,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
1415 1416 {
1416 1417 Pl_Buffer bufpl("encrypted string");
1417 1418 Pl_AES_PDF pl("aes encrypt string", &bufpl, true,
1418   - (unsigned char const*)this->cur_data_key.c_str());
  1419 + (unsigned char const*)this->cur_data_key.c_str(),
  1420 + (unsigned int)this->cur_data_key.length());
1419 1421 pl.write((unsigned char*) val.c_str(), val.length());
1420 1422 pl.finish();
1421 1423 Buffer* buf = bufpl.getBuffer();
... ...
libqpdf/QPDF_encryption.cc
... ... @@ -674,10 +674,10 @@ QPDF::decryptString(std::string&amp; str, int objid, int generation)
674 674 if (use_aes)
675 675 {
676 676 QTC::TC("qpdf", "QPDF_encryption aes decode string");
677   - assert(key.length() == Pl_AES_PDF::key_size);
678 677 Pl_Buffer bufpl("decrypted string");
679 678 Pl_AES_PDF pl("aes decrypt string", &bufpl, false,
680   - (unsigned char const*)key.c_str());
  679 + (unsigned char const*)key.c_str(),
  680 + (unsigned int)key.length());
681 681 pl.write((unsigned char*)str.c_str(), str.length());
682 682 pl.finish();
683 683 PointerHolder<Buffer> buf = bufpl.getBuffer();
... ... @@ -794,15 +794,16 @@ QPDF::decryptStream(Pipeline*&amp; pipeline, int objid, int generation,
794 794 if (use_aes)
795 795 {
796 796 QTC::TC("qpdf", "QPDF_encryption aes decode stream");
797   - assert(key.length() == Pl_AES_PDF::key_size);
798 797 pipeline = new Pl_AES_PDF("AES stream decryption", pipeline,
799   - false, (unsigned char*) key.c_str());
  798 + false, (unsigned char*) key.c_str(),
  799 + (unsigned int) key.length());
800 800 }
801 801 else
802 802 {
803 803 QTC::TC("qpdf", "QPDF_encryption rc4 decode stream");
804 804 pipeline = new Pl_RC4("RC4 stream decryption", pipeline,
805   - (unsigned char*) key.c_str(), (int)key.length());
  805 + (unsigned char*) key.c_str(),
  806 + (unsigned int) key.length());
806 807 }
807 808 heap.push_back(pipeline);
808 809 }
... ...
libqpdf/qpdf/Pl_AES_PDF.hh
... ... @@ -7,17 +7,16 @@
7 7 # include <stdint.h>
8 8 #endif
9 9  
10   -// This pipeline implements AES-128 with CBC and block padding as
11   -// specified in the PDF specification.
  10 +// This pipeline implements AES-128 and AES-256 with CBC and block
  11 +// padding as specified in the PDF specification.
12 12  
13 13 class Pl_AES_PDF: public Pipeline
14 14 {
15 15 public:
16   - // key_data should be a pointer to key_size bytes of data
17   - static unsigned int const key_size = 16;
18 16 QPDF_DLL
  17 + // key should be a pointer to key_bytes bytes of data
19 18 Pl_AES_PDF(char const* identifier, Pipeline* next,
20   - bool encrypt, unsigned char const key[key_size]);
  19 + bool encrypt, unsigned char const* key, unsigned int key_bytes);
21 20 QPDF_DLL
22 21 virtual ~Pl_AES_PDF();
23 22  
... ... @@ -26,6 +25,13 @@ class Pl_AES_PDF: public Pipeline
26 25 QPDF_DLL
27 26 virtual void finish();
28 27  
  28 + // Use zero initialization vector; needed for AESV3
  29 + QPDF_DLL
  30 + void useZeroIV();
  31 + // Disable padding; needed for AESV3
  32 + QPDF_DLL
  33 + void disablePadding();
  34 +
29 35 // For testing only; PDF always uses CBC
30 36 QPDF_DLL
31 37 void disableCBC();
... ... @@ -44,12 +50,14 @@ class Pl_AES_PDF: public Pipeline
44 50 bool cbc_mode;
45 51 bool first;
46 52 size_t offset; // offset into memory buffer
47   - unsigned char key[key_size];
48   - uint32_t rk[key_size + 28];
  53 + unsigned char* key;
  54 + uint32_t* rk;
49 55 unsigned char inbuf[buf_size];
50 56 unsigned char outbuf[buf_size];
51 57 unsigned char cbc_block[buf_size];
52 58 unsigned int nrounds;
  59 + bool use_zero_iv;
  60 + bool disable_padding;
53 61 };
54 62  
55 63 #endif // __PL_AES_PDF_HH__
... ...
libtests/aes.cc
... ... @@ -8,52 +8,86 @@
8 8  
9 9 static void usage()
10 10 {
11   - std::cerr << "Usage: aes [+-]cbc { -encrypt | -decrypt }"
12   - << " hex-key infile outfile" << std::endl;
  11 + std::cerr << "Usage: aes options hex-key infile outfile" << std::endl
  12 + << " -cbc -- disable CBC mode" << std::endl
  13 + << " +cbc -- enable CBC mode" << std::endl
  14 + << " -encrypt -- encrypt" << std::endl
  15 + << " -decrypt -- decrypt CBC mode" << std::endl
  16 + << " -zero-iv -- use zero initialization vector" << std::endl
  17 + << " -static-iv -- use static initialization vector" << std::endl
  18 + << " -no-padding -- disable padding" << std::endl
  19 + << "Options must precede key and file names." << std::endl;
13 20 exit(2);
14 21 }
15 22  
16 23 int main(int argc, char* argv[])
17 24 {
18   - if (argc != 6)
19   - {
20   - usage();
21   - }
22   -
23   - char* cbc = argv[1];
24   - char* action = argv[2];
25   - char* hexkey = argv[3];
26   - char* infilename = argv[4];
27   - char* outfilename = argv[5];
28   -
  25 + bool encrypt = true;
29 26 bool cbc_mode = true;
30   - if (strcmp(cbc, "-cbc") == 0)
31   - {
32   - cbc_mode = false;
33   - }
34   - else if (strcmp(cbc, "+cbc") != 0)
35   - {
36   - usage();
37   - }
  27 + char* hexkey = 0;
  28 + char* infilename = 0;
  29 + char* outfilename = 0;
  30 + bool zero_iv = false;
  31 + bool static_iv = false;
  32 + bool disable_padding = false;
38 33  
39   - bool encrypt = true;
40   - if (strcmp(action, "-decrypt") == 0)
  34 + for (int i = 1; i < argc; ++i)
41 35 {
42   - encrypt = false;
  36 + char* arg = argv[i];
  37 + if ((arg[0] == '-') || (arg[0] == '+'))
  38 + {
  39 + if (strcmp(arg, "-cbc") == 0)
  40 + {
  41 + cbc_mode = false;
  42 + }
  43 + else if (strcmp(arg, "+cbc") == 0)
  44 + {
  45 + cbc_mode = true;
  46 + }
  47 + else if (strcmp(arg, "-decrypt") == 0)
  48 + {
  49 + encrypt = false;
  50 + }
  51 + else if (strcmp(arg, "-encrypt") == 0)
  52 + {
  53 + encrypt = true;
  54 + }
  55 + else if (strcmp(arg, "-zero-iv") == 0)
  56 + {
  57 + zero_iv = true;
  58 + }
  59 + else if (strcmp(arg, "-static-iv") == 0)
  60 + {
  61 + static_iv = true;
  62 + }
  63 + else if (strcmp(arg, "-no-padding") == 0)
  64 + {
  65 + disable_padding = true;
  66 + }
  67 + else
  68 + {
  69 + usage();
  70 + }
  71 + }
  72 + else if (argc == i + 3)
  73 + {
  74 + hexkey = argv[i];
  75 + infilename = argv[i+1];
  76 + outfilename = argv[i+2];
  77 + break;
  78 + }
  79 + else
  80 + {
  81 + usage();
  82 + }
43 83 }
44   - else if (strcmp(action, "-encrypt") != 0)
  84 + if (outfilename == 0)
45 85 {
46   - usage();
  86 + usage();
47 87 }
48 88  
49 89 unsigned int hexkeylen = (unsigned int)strlen(hexkey);
50 90 unsigned int keylen = hexkeylen / 2;
51   - if (keylen != Pl_AES_PDF::key_size)
52   - {
53   - std::cerr << "key length must be " << Pl_AES_PDF::key_size
54   - << " bytes" << std::endl;
55   - exit(2);
56   - }
57 91  
58 92 FILE* infile = fopen(infilename, "rb");
59 93 if (infile == 0)
... ... @@ -69,7 +103,7 @@ int main(int argc, char* argv[])
69 103 exit(2);
70 104 }
71 105  
72   - unsigned char key[Pl_AES_PDF::key_size];
  106 + unsigned char* key = new unsigned char[keylen];
73 107 for (unsigned int i = 0; i < strlen(hexkey); i += 2)
74 108 {
75 109 char t[3];
... ... @@ -82,11 +116,25 @@ int main(int argc, char* argv[])
82 116 }
83 117  
84 118 Pl_StdioFile* out = new Pl_StdioFile("stdout", outfile);
85   - Pl_AES_PDF* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, key);
  119 + Pl_AES_PDF* aes = new Pl_AES_PDF("aes_128_cbc", out, encrypt, key, keylen);
  120 + delete [] key;
  121 + key = 0;
86 122 if (! cbc_mode)
87 123 {
88 124 aes->disableCBC();
89 125 }
  126 + if (zero_iv)
  127 + {
  128 + aes->useZeroIV();
  129 + }
  130 + else if (static_iv)
  131 + {
  132 + aes->useStaticIV();
  133 + }
  134 + if (disable_padding)
  135 + {
  136 + aes->disablePadding();
  137 + }
90 138  
91 139 // 16 < buffer size, buffer_size is not a multiple of 8 for testing
92 140 unsigned char buf[83];
... ...