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:
parent
774584163f
commit
9b42f526df
@ -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
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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__
|
||||||
|
104
libtests/aes.cc
104
libtests/aes.cc
@ -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];
|
||||||
|
Loading…
Reference in New Issue
Block a user