From ce8f9b6608ebcc3e88fbb1655be3e9363fa671b6 Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sun, 3 Nov 2019 21:22:03 -0500 Subject: [PATCH] MD5: switch to pluggable crypto --- include/qpdf/QPDFCryptoImpl.hh | 11 ++ libqpdf/MD5.cc | 163 +++++++++++++++++++++++++ libqpdf/MD5_native.cc | 194 +++++------------------------- libqpdf/QPDFCrypto_native.cc | 26 ++++ libqpdf/build.mk | 1 + libqpdf/qpdf/MD5.hh | 70 +++++++++++ libqpdf/qpdf/MD5_native.hh | 92 +++----------- libqpdf/qpdf/QPDFCrypto_native.hh | 10 ++ 8 files changed, 329 insertions(+), 238 deletions(-) create mode 100644 libqpdf/MD5.cc create mode 100644 libqpdf/qpdf/MD5.hh diff --git a/include/qpdf/QPDFCryptoImpl.hh b/include/qpdf/QPDFCryptoImpl.hh index 4da299aa..8fa8b21e 100644 --- a/include/qpdf/QPDFCryptoImpl.hh +++ b/include/qpdf/QPDFCryptoImpl.hh @@ -23,6 +23,7 @@ #define QPDFCRYPTOIMPL_HH #include +#include // 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 diff --git a/libqpdf/MD5.cc b/libqpdf/MD5.cc new file mode 100644 index 00000000..95edd3a4 --- /dev/null +++ b/libqpdf/MD5.cc @@ -0,0 +1,163 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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(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; +} diff --git a/libqpdf/MD5_native.cc b/libqpdf/MD5_native.cc index 6e166a7c..d4187e8c 100644 --- a/libqpdf/MD5_native.cc +++ b/libqpdf/MD5_native.cc @@ -27,7 +27,7 @@ // ///////////////////////////////////////////////////////////////////////// -#include +#include #include #include @@ -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(ac); \ + (a) += F ((b), (c), (d)) + (x) + static_cast(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(ac); \ + (a) += G ((b), (c), (d)) + (x) + static_cast(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(ac); \ + (a) += H ((b), (c), (d)) + (x) + static_cast(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(ac); \ + (a) += I ((b), (c), (d)) + (x) + static_cast(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((count[0] >> 3) & 0x3f); // Update number of bits - if ((count[0] += (static_cast(inputLen) << 3)) < - (static_cast(inputLen) << 3)) + if ((count[0] += (static_cast(inputLen) << 3)) < + (static_cast(inputLen) << 3)) count[1]++; - count[1] += (static_cast(inputLen) >> 29); + count[1] += (static_cast(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(input[j]) | - (static_cast(input[j+1]) << 8) | - (static_cast(input[j+2]) << 16) | - (static_cast(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(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(input[j]) | + (static_cast(input[j+1]) << 8) | + (static_cast(input[j+2]) << 16) | + (static_cast(input[j+3]) << 24); } diff --git a/libqpdf/QPDFCrypto_native.cc b/libqpdf/QPDFCrypto_native.cc index ab9fd3a2..1ee73782 100644 --- a/libqpdf/QPDFCrypto_native.cc +++ b/libqpdf/QPDFCrypto_native.cc @@ -1 +1,27 @@ #include +#include + +void +QPDFCrypto_native::MD5_init() +{ + this->md5 = std::make_shared(); +} + +void +QPDFCrypto_native::MD5_update(unsigned char const* data, size_t len) +{ + this->md5->update(const_cast(data), len); +} + +void +QPDFCrypto_native::MD5_finalize() +{ + this->md5->finalize(); +} + +void +QPDFCrypto_native::MD5_digest(MD5_Digest d) +{ + this->md5->digest(d); +} + diff --git a/libqpdf/build.mk b/libqpdf/build.mk index d2bcd8c5..9f1244d0 100644 --- a/libqpdf/build.mk +++ b/libqpdf/build.mk @@ -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 \ diff --git a/libqpdf/qpdf/MD5.hh b/libqpdf/qpdf/MD5.hh new file mode 100644 index 00000000..d04a89b4 --- /dev/null +++ b/libqpdf/qpdf/MD5.hh @@ -0,0 +1,70 @@ +#ifndef MD5_HH +#define MD5_HH + +#include +#include +#include +#include +#include + +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 crypto; +}; + +#endif // MD5_HH diff --git a/libqpdf/qpdf/MD5_native.hh b/libqpdf/qpdf/MD5_native.hh index 02cdb3cf..d7e65a50 100644 --- a/libqpdf/qpdf/MD5_native.hh +++ b/libqpdf/qpdf/MD5_native.hh @@ -1,94 +1,32 @@ -#ifndef MD5_HH -#define MD5_HH +#ifndef MD5_NATIVE_HH +#define MD5_NATIVE_HH -#include #include -#include -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#include +#include +#include -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 diff --git a/libqpdf/qpdf/QPDFCrypto_native.hh b/libqpdf/qpdf/QPDFCrypto_native.hh index 785d6c6f..77179075 100644 --- a/libqpdf/qpdf/QPDFCrypto_native.hh +++ b/libqpdf/qpdf/QPDFCrypto_native.hh @@ -3,6 +3,8 @@ #include #include +#include +#include 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; }; #endif // QPDFCRYPTO_NATIVE_HH