From 9b42f526dffd5e1518ff3d83564eb09111ac5f0d Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Thu, 27 Dec 2012 21:37:57 -0500 Subject: Update AES classes to work with 256-bit keys --- libqpdf/Pl_AES_PDF.cc | 77 ++++++++++++++++++++++-------- libqpdf/QPDFWriter.cc | 8 ++-- libqpdf/QPDF_encryption.cc | 11 +++-- libqpdf/qpdf/Pl_AES_PDF.hh | 22 ++++++--- libtests/aes.cc | 116 ++++++++++++++++++++++++++++++++------------- 5 files changed, 165 insertions(+), 69 deletions(-) diff --git a/libqpdf/Pl_AES_PDF.cc b/libqpdf/Pl_AES_PDF.cc index 0f73c09c..57bf5766 100644 --- a/libqpdf/Pl_AES_PDF.cc +++ b/libqpdf/Pl_AES_PDF.cc @@ -15,19 +15,24 @@ 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), + 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 +49,20 @@ 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 @@ -90,13 +108,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 +133,7 @@ Pl_AES_PDF::finish() this->buf_size - this->offset); this->offset = this->buf_size; } - flush(true); + flush(! this->disable_padding); } getNext()->finish(); } @@ -136,6 +157,13 @@ Pl_AES_PDF::initializeVector() this->cbc_block[i] = 14 * (1 + i); } } + else if (use_zero_iv) + { + for (unsigned int i = 0; i < this->buf_size; ++i) + { + this->cbc_block[i] = 0; + } + } else { for (unsigned int i = 0; i < this->buf_size; ++i) @@ -157,12 +185,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) + { + getNext()->write(this->cbc_block, this->buf_size); + } } - else + else if (this->use_zero_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/QPDFWriter.cc b/libqpdf/QPDFWriter.cc index 963f9627..cde76af9 100644 --- a/libqpdf/QPDFWriter.cc +++ b/libqpdf/QPDFWriter.cc @@ -815,13 +815,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); } @@ -1415,7 +1416,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(); diff --git a/libqpdf/QPDF_encryption.cc b/libqpdf/QPDF_encryption.cc index 893f93f5..43bc62ba 100644 --- a/libqpdf/QPDF_encryption.cc +++ b/libqpdf/QPDF_encryption.cc @@ -674,10 +674,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(); @@ -794,15 +794,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); } diff --git a/libqpdf/qpdf/Pl_AES_PDF.hh b/libqpdf/qpdf/Pl_AES_PDF.hh index 3947506b..72f82229 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,13 @@ 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(); + // For testing only; PDF always uses CBC QPDF_DLL void disableCBC(); @@ -44,12 +50,14 @@ 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 int nrounds; + bool use_zero_iv; + bool disable_padding; }; #endif // __PL_AES_PDF_HH__ diff --git a/libtests/aes.cc b/libtests/aes.cc index ad2f0dd8..381148c1 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]; -- cgit v1.2.3-54-g00ecf