Commit 9b42f526dffd5e1518ff3d83564eb09111ac5f0d
1 parent
77458416
Update AES classes to work with 256-bit keys
Showing
5 changed files
with
165 additions
and
69 deletions
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& 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*& 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]; | ... | ... |