MD5: switch to pluggable crypto

This commit is contained in:
Jay Berkenbilt 2019-11-03 21:22:03 -05:00
parent 5c3e856e9f
commit ce8f9b6608
8 changed files with 329 additions and 238 deletions

View File

@ -23,6 +23,7 @@
#define QPDFCRYPTOIMPL_HH
#include <qpdf/DLL.h>
#include <cstring>
// This class is part of qpdf's pluggable crypto provider support.
// Most users won't need to know or care about this class, but you can
@ -39,6 +40,16 @@ class QPDF_DLL_CLASS QPDFCryptoImpl
QPDF_DLL
virtual ~QPDFCryptoImpl() = default;
typedef unsigned char MD5_Digest[16];
QPDF_DLL
virtual void MD5_init() = 0;
QPDF_DLL
virtual void MD5_update(unsigned char const* data, size_t len) = 0;
QPDF_DLL
virtual void MD5_finalize() = 0;
QPDF_DLL
virtual void MD5_digest(MD5_Digest) = 0;
};
#endif // QPDFCRYPTOIMPL_HH

163
libqpdf/MD5.cc Normal file
View File

@ -0,0 +1,163 @@
#include <qpdf/MD5.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QIntC.hh>
#include <qpdf/QPDFCryptoProvider.hh>
#include <stdio.h>
#include <memory.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
MD5::MD5()
{
init();
}
void
MD5::init()
{
this->crypto = QPDFCryptoProvider::getImpl();
this->crypto->MD5_init();
}
void
MD5::finalize()
{
this->crypto->MD5_finalize();
}
void MD5::reset()
{
init();
}
void MD5::encodeString(char const* str)
{
size_t len = strlen(str);
crypto->MD5_init();
encodeDataIncrementally(str, len);
crypto->MD5_finalize();
}
void MD5::appendString(char const* input_string)
{
encodeDataIncrementally(input_string, strlen(input_string));
}
void MD5::encodeDataIncrementally(char const* data, size_t len)
{
this->crypto->MD5_update(QUtil::unsigned_char_pointer(data), len);
}
void MD5::encodeFile(char const *filename, qpdf_offset_t up_to_offset)
{
char buffer[1024];
FILE *file = QUtil::safe_fopen(filename, "rb");
size_t len;
size_t so_far = 0;
size_t to_try = 1024;
size_t up_to_size = 0;
if (up_to_offset >= 0)
{
up_to_size = QIntC::to_size(up_to_offset);
}
do
{
if ((up_to_offset >= 0) && ((so_far + to_try) > up_to_size))
{
to_try = up_to_size - so_far;
}
len = fread(buffer, 1, to_try, file);
if (len > 0)
{
encodeDataIncrementally(buffer, len);
so_far += len;
if ((up_to_offset >= 0) && (so_far >= up_to_size))
{
break;
}
}
} while (len > 0);
if (ferror(file))
{
// Assume, perhaps incorrectly, that errno was set by the
// underlying call to read....
(void) fclose(file);
QUtil::throw_system_error(
std::string("MD5: read error on ") + filename);
}
(void) fclose(file);
this->crypto->MD5_finalize();
}
void MD5::digest(Digest result)
{
this->crypto->MD5_finalize();
this->crypto->MD5_digest(result);
}
void MD5::print()
{
Digest digest_val;
digest(digest_val);
unsigned int i;
for (i = 0; i < 16; ++i)
{
printf("%02x", digest_val[i]);
}
printf("\n");
}
std::string MD5::unparse()
{
this->crypto->MD5_finalize();
Digest digest_val;
digest(digest_val);
return QUtil::hex_encode(
std::string(reinterpret_cast<char*>(digest_val), 16));
}
std::string
MD5::getDataChecksum(char const* buf, size_t len)
{
MD5 m;
m.encodeDataIncrementally(buf, len);
return m.unparse();
}
std::string
MD5::getFileChecksum(char const* filename, qpdf_offset_t up_to_offset)
{
MD5 m;
m.encodeFile(filename, up_to_offset);
return m.unparse();
}
bool
MD5::checkDataChecksum(char const* const checksum,
char const* buf, size_t len)
{
std::string actual_checksum = getDataChecksum(buf, len);
return (checksum == actual_checksum);
}
bool
MD5::checkFileChecksum(char const* const checksum,
char const* filename, qpdf_offset_t up_to_offset)
{
bool result = false;
try
{
std::string actual_checksum = getFileChecksum(filename, up_to_offset);
result = (checksum == actual_checksum);
}
catch (std::runtime_error const&)
{
// Ignore -- return false
}
return result;
}

View File

@ -27,7 +27,7 @@
//
/////////////////////////////////////////////////////////////////////////
#include <qpdf/MD5.hh>
#include <qpdf/MD5_native.hh>
#include <qpdf/QUtil.hh>
#include <qpdf/QIntC.hh>
@ -72,28 +72,33 @@ static unsigned char PADDING[64] = {
// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
// Rotation is separate from addition to prevent recomputation.
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + static_cast<UINT4>(ac); \
(a) += F ((b), (c), (d)) + (x) + static_cast<uint32_t>(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define GG(a, b, c, d, x, s, ac) { \
(a) += G ((b), (c), (d)) + (x) + static_cast<UINT4>(ac); \
(a) += G ((b), (c), (d)) + (x) + static_cast<uint32_t>(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define HH(a, b, c, d, x, s, ac) { \
(a) += H ((b), (c), (d)) + (x) + static_cast<UINT4>(ac); \
(a) += H ((b), (c), (d)) + (x) + static_cast<uint32_t>(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
#define II(a, b, c, d, x, s, ac) { \
(a) += I ((b), (c), (d)) + (x) + static_cast<UINT4>(ac); \
(a) += I ((b), (c), (d)) + (x) + static_cast<uint32_t>(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
(a) += (b); \
}
MD5_native::MD5_native()
{
init();
}
// MD5 initialization. Begins an MD5 operation, writing a new context.
void MD5::init()
void MD5_native::init()
{
count[0] = count[1] = 0;
// Load magic initialization constants.
@ -110,7 +115,7 @@ void MD5::init()
// operation, processing another message block, and updating the
// context.
void MD5::update(unsigned char *input,
void MD5_native::update(unsigned char *input,
size_t inputLen)
{
unsigned int i, index, partLen;
@ -119,10 +124,10 @@ void MD5::update(unsigned char *input,
index = static_cast<unsigned int>((count[0] >> 3) & 0x3f);
// Update number of bits
if ((count[0] += (static_cast<UINT4>(inputLen) << 3)) <
(static_cast<UINT4>(inputLen) << 3))
if ((count[0] += (static_cast<uint32_t>(inputLen) << 3)) <
(static_cast<uint32_t>(inputLen) << 3))
count[1]++;
count[1] += (static_cast<UINT4>(inputLen) >> 29);
count[1] += (static_cast<uint32_t>(inputLen) >> 29);
partLen = 64 - index;
@ -146,7 +151,7 @@ void MD5::update(unsigned char *input,
// MD5 finalization. Ends an MD5 message-digest operation, writing the
// the message digest and zeroizing the context.
void MD5::final()
void MD5_native::finalize()
{
if (finalized)
{
@ -178,10 +183,16 @@ void MD5::final()
finalized = true;
}
// MD5 basic transformation. Transforms state based on block.
void MD5::transform(UINT4 state[4], unsigned char block[64])
void
MD5_native::digest(Digest result)
{
UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
memcpy(result, digest_val, sizeof(digest_val));
}
// MD5 basic transformation. Transforms state based on block.
void MD5_native::transform(uint32_t state[4], unsigned char block[64])
{
uint32_t a = state[0], b = state[1], c = state[2], d = state[3], x[16];
decode(x, block, 64);
@ -267,9 +278,9 @@ void MD5::transform(UINT4 state[4], unsigned char block[64])
memset (x, 0, sizeof (x));
}
// Encodes input (UINT4) into output (unsigned char). Assumes len is a
// Encodes input (uint32_t) into output (unsigned char). Assumes len is a
// multiple of 4.
void MD5::encode(unsigned char *output, UINT4 *input, size_t len)
void MD5_native::encode(unsigned char *output, uint32_t *input, size_t len)
{
unsigned int i, j;
@ -281,155 +292,16 @@ void MD5::encode(unsigned char *output, UINT4 *input, size_t len)
}
}
// Decodes input (unsigned char) into output (UINT4). Assumes len is a
// Decodes input (unsigned char) into output (uint32_t). Assumes len is a
// multiple of 4.
void MD5::decode(UINT4 *output, unsigned char *input, size_t len)
void MD5_native::decode(uint32_t *output, unsigned char *input, size_t len)
{
unsigned int i, j;
for (i = 0, j = 0; j < len; i++, j += 4)
output[i] =
static_cast<UINT4>(input[j]) |
(static_cast<UINT4>(input[j+1]) << 8) |
(static_cast<UINT4>(input[j+2]) << 16) |
(static_cast<UINT4>(input[j+3]) << 24);
}
// Public functions
MD5::MD5()
{
init();
}
void MD5::reset()
{
init();
}
void MD5::encodeString(char const* str)
{
size_t len = strlen(str);
update(QUtil::unsigned_char_pointer(str), len);
final();
}
void MD5::appendString(char const* input_string)
{
update(QUtil::unsigned_char_pointer(input_string), strlen(input_string));
}
void MD5::encodeDataIncrementally(char const* data, size_t len)
{
update(QUtil::unsigned_char_pointer(data), len);
}
void MD5::encodeFile(char const *filename, qpdf_offset_t up_to_offset)
{
unsigned char buffer[1024];
FILE *file = QUtil::safe_fopen(filename, "rb");
size_t len;
size_t so_far = 0;
size_t to_try = 1024;
size_t up_to_size = 0;
if (up_to_offset >= 0)
{
up_to_size = QIntC::to_size(up_to_offset);
}
do
{
if ((up_to_offset >= 0) && ((so_far + to_try) > up_to_size))
{
to_try = up_to_size - so_far;
}
len = fread(buffer, 1, to_try, file);
if (len > 0)
{
update(buffer, len);
so_far += len;
if ((up_to_offset >= 0) && (so_far >= up_to_size))
{
break;
}
}
} while (len > 0);
if (ferror(file))
{
// Assume, perhaps incorrectly, that errno was set by the
// underlying call to read....
(void) fclose(file);
QUtil::throw_system_error(
std::string("MD5: read error on ") + filename);
}
(void) fclose(file);
final();
}
void MD5::digest(Digest result)
{
final();
memcpy(result, digest_val, sizeof(digest_val));
}
void MD5::print()
{
final();
unsigned int i;
for (i = 0; i < 16; ++i)
{
printf("%02x", digest_val[i]);
}
printf("\n");
}
std::string MD5::unparse()
{
final();
return QUtil::hex_encode(
std::string(reinterpret_cast<char*>(digest_val), 16));
}
std::string
MD5::getDataChecksum(char const* buf, size_t len)
{
MD5 m;
m.encodeDataIncrementally(buf, len);
return m.unparse();
}
std::string
MD5::getFileChecksum(char const* filename, qpdf_offset_t up_to_offset)
{
MD5 m;
m.encodeFile(filename, up_to_offset);
return m.unparse();
}
bool
MD5::checkDataChecksum(char const* const checksum,
char const* buf, size_t len)
{
std::string actual_checksum = getDataChecksum(buf, len);
return (checksum == actual_checksum);
}
bool
MD5::checkFileChecksum(char const* const checksum,
char const* filename, qpdf_offset_t up_to_offset)
{
bool result = false;
try
{
std::string actual_checksum = getFileChecksum(filename, up_to_offset);
result = (checksum == actual_checksum);
}
catch (std::runtime_error const&)
{
// Ignore -- return false
}
return result;
static_cast<uint32_t>(input[j]) |
(static_cast<uint32_t>(input[j+1]) << 8) |
(static_cast<uint32_t>(input[j+2]) << 16) |
(static_cast<uint32_t>(input[j+3]) << 24);
}

View File

@ -1 +1,27 @@
#include <qpdf/QPDFCrypto_native.hh>
#include <qpdf/QUtil.hh>
void
QPDFCrypto_native::MD5_init()
{
this->md5 = std::make_shared<MD5_native>();
}
void
QPDFCrypto_native::MD5_update(unsigned char const* data, size_t len)
{
this->md5->update(const_cast<unsigned char*>(data), len);
}
void
QPDFCrypto_native::MD5_finalize()
{
this->md5->finalize();
}
void
QPDFCrypto_native::MD5_digest(MD5_Digest d)
{
this->md5->digest(d);
}

View File

@ -16,6 +16,7 @@ SRCS_libqpdf = \
libqpdf/InsecureRandomDataProvider.cc \
libqpdf/JSON.cc \
libqpdf/MD5.cc \
libqpdf/MD5_native.cc \
libqpdf/OffsetInputSource.cc \
libqpdf/Pipeline.cc \
libqpdf/Pl_AES_PDF.cc \

70
libqpdf/qpdf/MD5.hh Normal file
View File

@ -0,0 +1,70 @@
#ifndef MD5_HH
#define MD5_HH
#include <qpdf/DLL.h>
#include <qpdf/Types.h>
#include <qpdf/QPDFCryptoImpl.hh>
#include <string>
#include <memory>
class MD5
{
public:
typedef unsigned char Digest[16];
QPDF_DLL
MD5();
QPDF_DLL
void reset();
// encodes string and finalizes
QPDF_DLL
void encodeString(char const* input_string);
// encodes file and finalizes; offset < 0 reads whole file
QPDF_DLL
void encodeFile(char const* filename, qpdf_offset_t up_to_offset = -1);
// appends string to current md5 object
QPDF_DLL
void appendString(char const* input_string);
// appends arbitrary data to current md5 object
QPDF_DLL
void encodeDataIncrementally(char const* input_data, size_t len);
// computes a raw digest
QPDF_DLL
void digest(Digest);
// prints the digest to stdout terminated with \r\n (primarily for
// testing)
QPDF_DLL
void print();
// returns the digest as a hexadecimal string
QPDF_DLL
std::string unparse();
// Convenience functions
QPDF_DLL
static std::string getDataChecksum(char const* buf, size_t len);
QPDF_DLL
static std::string getFileChecksum(char const* filename,
qpdf_offset_t up_to_offset = -1);
QPDF_DLL
static bool checkDataChecksum(char const* const checksum,
char const* buf, size_t len);
QPDF_DLL
static bool checkFileChecksum(char const* const checksum,
char const* filename,
qpdf_offset_t up_to_offset = -1);
private:
void init();
void finalize();
std::shared_ptr<QPDFCryptoImpl> crypto;
};
#endif // MD5_HH

View File

@ -1,94 +1,32 @@
#ifndef MD5_HH
#define MD5_HH
#ifndef MD5_NATIVE_HH
#define MD5_NATIVE_HH
#include <qpdf/DLL.h>
#include <qpdf/qpdf-config.h>
#include <qpdf/Types.h>
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#include <string>
#include <cstdint>
#include <cstring>
class MD5
class MD5_native
{
public:
typedef unsigned char Digest[16];
QPDF_DLL
MD5();
QPDF_DLL
void reset();
// encodes string and finalizes
QPDF_DLL
void encodeString(char const* input_string);
// encodes file and finalizes; offset < 0 reads whole file
QPDF_DLL
void encodeFile(char const* filename, qpdf_offset_t up_to_offset = -1);
// appends string to current md5 object
QPDF_DLL
void appendString(char const* input_string);
// appends arbitrary data to current md5 object
QPDF_DLL
void encodeDataIncrementally(char const* input_data, size_t len);
// computes a raw digest
QPDF_DLL
void digest(Digest);
// prints the digest to stdout terminated with \r\n (primarily for
// testing)
QPDF_DLL
void print();
// returns the digest as a hexadecimal string
QPDF_DLL
std::string unparse();
// Convenience functions
QPDF_DLL
static std::string getDataChecksum(char const* buf, size_t len);
QPDF_DLL
static std::string getFileChecksum(char const* filename,
qpdf_offset_t up_to_offset = -1);
QPDF_DLL
static bool checkDataChecksum(char const* const checksum,
char const* buf, size_t len);
QPDF_DLL
static bool checkFileChecksum(char const* const checksum,
char const* filename,
qpdf_offset_t up_to_offset = -1);
private:
// POINTER defines a generic pointer type
typedef void *POINTER;
// UINT2 defines a two byte word
typedef uint16_t UINT2;
// UINT4 defines a four byte word
typedef uint32_t UINT4;
MD5_native();
void init();
void update(unsigned char *, size_t);
void final();
void finalize();
void digest(Digest);
static void transform(UINT4 [4], unsigned char [64]);
static void encode(unsigned char *, UINT4 *, size_t);
static void decode(UINT4 *, unsigned char *, size_t);
private:
static void transform(uint32_t[4], unsigned char[64]);
static void encode(unsigned char *, uint32_t *, size_t);
static void decode(uint32_t *, unsigned char *, size_t);
UINT4 state[4]; // state (ABCD)
UINT4 count[2]; // number of bits, modulo 2^64 (lsb first)
uint32_t state[4]; // state (ABCD)
uint32_t count[2]; // number of bits, modulo 2^64 (lsb first)
unsigned char buffer[64]; // input buffer
bool finalized;
Digest digest_val;
};
#endif // MD5_HH
#endif // MD5_NATIVE_HH

View File

@ -3,6 +3,8 @@
#include <qpdf/DLL.h>
#include <qpdf/QPDFCryptoImpl.hh>
#include <qpdf/MD5_native.hh>
#include <memory>
class QPDFCrypto_native: public QPDFCryptoImpl
{
@ -11,6 +13,14 @@ class QPDFCrypto_native: public QPDFCryptoImpl
QPDF_DLL
virtual ~QPDFCrypto_native() = default;
virtual void MD5_init();
virtual void MD5_update(unsigned char const* data, size_t len);
virtual void MD5_finalize();
virtual void MD5_digest(MD5_Digest);
private:
std::shared_ptr<MD5_native> md5;
};
#endif // QPDFCRYPTO_NATIVE_HH