2
1
mirror of https://github.com/qpdf/qpdf.git synced 2025-01-08 17:24:06 +00:00

Update AES classes to work with 256-bit keys

This commit is contained in:
Jay Berkenbilt 2012-12-27 21:37:57 -05:00
parent 774584163f
commit 9b42f526df
5 changed files with 166 additions and 70 deletions

View File

@ -15,19 +15,24 @@
bool Pl_AES_PDF::use_static_iv = false; bool Pl_AES_PDF::use_static_iv = false;
Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, 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), Pipeline(identifier, next),
encrypt(encrypt), encrypt(encrypt),
cbc_mode(true), cbc_mode(true),
first(true), first(true),
offset(0), offset(0),
nrounds(0) nrounds(0),
use_zero_iv(false),
disable_padding(false)
{ {
static int const keybits = 128; unsigned int keybits = 8 * key_bytes;
assert(key_size == KEYLENGTH(keybits)); assert(key_bytes == KEYLENGTH(keybits));
assert(sizeof(this->rk) / sizeof(uint32_t) == RKLENGTH(keybits)); this->key = new unsigned char[key_bytes];
std::memcpy(this->key, key, key_size); this->rk = new uint32_t[RKLENGTH(keybits)];
std::memset(this->rk, 0, sizeof(this->rk)); 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->inbuf, 0, this->buf_size);
std::memset(this->outbuf, 0, this->buf_size); std::memset(this->outbuf, 0, this->buf_size);
std::memset(this->cbc_block, 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() 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 void
@ -90,6 +108,8 @@ Pl_AES_PDF::finish()
{ {
flush(false); flush(false);
} }
if (! this->disable_padding)
{
// Pad as described in section 3.5.1 of version 1.7 of the PDF // Pad as described in section 3.5.1 of version 1.7 of the PDF
// specification, including providing an entire block of padding // specification, including providing an entire block of padding
// if the input was a multiple of 16 bytes. // if the input was a multiple of 16 bytes.
@ -98,6 +118,7 @@ Pl_AES_PDF::finish()
this->offset = this->buf_size; this->offset = this->buf_size;
flush(false); flush(false);
} }
}
else else
{ {
if (this->offset != this->buf_size) if (this->offset != this->buf_size)
@ -112,7 +133,7 @@ Pl_AES_PDF::finish()
this->buf_size - this->offset); this->buf_size - this->offset);
this->offset = this->buf_size; this->offset = this->buf_size;
} }
flush(true); flush(! this->disable_padding);
} }
getNext()->finish(); getNext()->finish();
} }
@ -136,6 +157,13 @@ Pl_AES_PDF::initializeVector()
this->cbc_block[i] = 14 * (1 + i); 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 else
{ {
for (unsigned int i = 0; i < this->buf_size; ++i) for (unsigned int i = 0; i < this->buf_size; ++i)
@ -157,11 +185,20 @@ Pl_AES_PDF::flush(bool strip_padding)
{ {
if (encrypt) if (encrypt)
{ {
// Set cbc_block to a random initialization vector and // Set cbc_block to the initialization vector, and if
// write it to the output stream // not zero, write it to the output stream.
initializeVector(); initializeVector();
if (! this->use_zero_iv)
{
getNext()->write(this->cbc_block, this->buf_size); getNext()->write(this->cbc_block, this->buf_size);
} }
}
else if (this->use_zero_iv)
{
// Initialize vector with zeroes; zero vector was not
// written to the beginning of the input file.
initializeVector();
}
else else
{ {
// Take the first block of input as the initialization // Take the first block of input as the initialization

View File

@ -815,13 +815,14 @@ QPDFWriter::pushEncryptionFilter()
{ {
p = new Pl_AES_PDF( p = new Pl_AES_PDF(
"aes stream encryption", this->pipeline, true, "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 else
{ {
p = new Pl_RC4("rc4 stream encryption", this->pipeline, p = new Pl_RC4("rc4 stream encryption", this->pipeline,
(unsigned char*) this->cur_data_key.c_str(), (unsigned char*) this->cur_data_key.c_str(),
(int)this->cur_data_key.length()); (unsigned int)this->cur_data_key.length());
} }
pushPipeline(p); pushPipeline(p);
} }
@ -1415,7 +1416,8 @@ QPDFWriter::unparseObject(QPDFObjectHandle object, int level,
{ {
Pl_Buffer bufpl("encrypted string"); Pl_Buffer bufpl("encrypted string");
Pl_AES_PDF pl("aes encrypt string", &bufpl, true, 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.write((unsigned char*) val.c_str(), val.length());
pl.finish(); pl.finish();
Buffer* buf = bufpl.getBuffer(); Buffer* buf = bufpl.getBuffer();

View File

@ -674,10 +674,10 @@ QPDF::decryptString(std::string& str, int objid, int generation)
if (use_aes) if (use_aes)
{ {
QTC::TC("qpdf", "QPDF_encryption aes decode string"); QTC::TC("qpdf", "QPDF_encryption aes decode string");
assert(key.length() == Pl_AES_PDF::key_size);
Pl_Buffer bufpl("decrypted string"); Pl_Buffer bufpl("decrypted string");
Pl_AES_PDF pl("aes decrypt string", &bufpl, false, 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.write((unsigned char*)str.c_str(), str.length());
pl.finish(); pl.finish();
PointerHolder<Buffer> buf = bufpl.getBuffer(); PointerHolder<Buffer> buf = bufpl.getBuffer();
@ -794,15 +794,16 @@ QPDF::decryptStream(Pipeline*& pipeline, int objid, int generation,
if (use_aes) if (use_aes)
{ {
QTC::TC("qpdf", "QPDF_encryption aes decode stream"); 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, 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 else
{ {
QTC::TC("qpdf", "QPDF_encryption rc4 decode stream"); QTC::TC("qpdf", "QPDF_encryption rc4 decode stream");
pipeline = new Pl_RC4("RC4 stream decryption", pipeline, 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); heap.push_back(pipeline);
} }

View File

@ -7,17 +7,16 @@
# include <stdint.h> # include <stdint.h>
#endif #endif
// This pipeline implements AES-128 with CBC and block padding as // This pipeline implements AES-128 and AES-256 with CBC and block
// specified in the PDF specification. // padding as specified in the PDF specification.
class Pl_AES_PDF: public Pipeline class Pl_AES_PDF: public Pipeline
{ {
public: public:
// key_data should be a pointer to key_size bytes of data
static unsigned int const key_size = 16;
QPDF_DLL QPDF_DLL
// key should be a pointer to key_bytes bytes of data
Pl_AES_PDF(char const* identifier, Pipeline* next, 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 QPDF_DLL
virtual ~Pl_AES_PDF(); virtual ~Pl_AES_PDF();
@ -26,6 +25,13 @@ class Pl_AES_PDF: public Pipeline
QPDF_DLL QPDF_DLL
virtual void finish(); 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 // For testing only; PDF always uses CBC
QPDF_DLL QPDF_DLL
void disableCBC(); void disableCBC();
@ -44,12 +50,14 @@ class Pl_AES_PDF: public Pipeline
bool cbc_mode; bool cbc_mode;
bool first; bool first;
size_t offset; // offset into memory buffer size_t offset; // offset into memory buffer
unsigned char key[key_size]; unsigned char* key;
uint32_t rk[key_size + 28]; uint32_t* rk;
unsigned char inbuf[buf_size]; unsigned char inbuf[buf_size];
unsigned char outbuf[buf_size]; unsigned char outbuf[buf_size];
unsigned char cbc_block[buf_size]; unsigned char cbc_block[buf_size];
unsigned int nrounds; unsigned int nrounds;
bool use_zero_iv;
bool disable_padding;
}; };
#endif // __PL_AES_PDF_HH__ #endif // __PL_AES_PDF_HH__

View File

@ -8,52 +8,86 @@
static void usage() static void usage()
{ {
std::cerr << "Usage: aes [+-]cbc { -encrypt | -decrypt }" std::cerr << "Usage: aes options hex-key infile outfile" << std::endl
<< " 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); exit(2);
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
if (argc != 6) bool encrypt = true;
{
usage();
}
char* cbc = argv[1];
char* action = argv[2];
char* hexkey = argv[3];
char* infilename = argv[4];
char* outfilename = argv[5];
bool cbc_mode = true; bool cbc_mode = true;
if (strcmp(cbc, "-cbc") == 0) char* hexkey = 0;
char* infilename = 0;
char* outfilename = 0;
bool zero_iv = false;
bool static_iv = false;
bool disable_padding = false;
for (int i = 1; i < argc; ++i)
{
char* arg = argv[i];
if ((arg[0] == '-') || (arg[0] == '+'))
{
if (strcmp(arg, "-cbc") == 0)
{ {
cbc_mode = false; cbc_mode = false;
} }
else if (strcmp(cbc, "+cbc") != 0) else if (strcmp(arg, "+cbc") == 0)
{ {
usage(); cbc_mode = true;
} }
else if (strcmp(arg, "-decrypt") == 0)
bool encrypt = true;
if (strcmp(action, "-decrypt") == 0)
{ {
encrypt = false; encrypt = false;
} }
else if (strcmp(action, "-encrypt") != 0) 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();
}
}
if (outfilename == 0)
{ {
usage(); usage();
} }
unsigned int hexkeylen = (unsigned int)strlen(hexkey); unsigned int hexkeylen = (unsigned int)strlen(hexkey);
unsigned int keylen = hexkeylen / 2; 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"); FILE* infile = fopen(infilename, "rb");
if (infile == 0) if (infile == 0)
@ -69,7 +103,7 @@ int main(int argc, char* argv[])
exit(2); 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) for (unsigned int i = 0; i < strlen(hexkey); i += 2)
{ {
char t[3]; char t[3];
@ -82,11 +116,25 @@ int main(int argc, char* argv[])
} }
Pl_StdioFile* out = new Pl_StdioFile("stdout", outfile); 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) if (! cbc_mode)
{ {
aes->disableCBC(); 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 // 16 < buffer size, buffer_size is not a multiple of 8 for testing
unsigned char buf[83]; unsigned char buf[83];