diff --git a/configure.ac b/configure.ac index a9f0316..6ff59ef 100644 --- a/configure.ac +++ b/configure.ac @@ -348,6 +348,36 @@ AC_ARG_ENABLE(show-failed-test-output, fi], [SHOW_FAILED_TEST_OUTPUT=0]) +AC_PATH_TOOL(PKGCONFIG, pkg-config) + +AC_MSG_CHECKING(if cryptography will be supplied by GNU TLS) +AC_ARG_ENABLE(gnutls, + AS_HELP_STRING([--enable-gnutls], + [enable cryptography by GNUTLS]), + [if test "$enableval" = "yes"; then + if test "x$PKGCONFIG" != x ; then + AC_TRY_COMPILE([ + #include ], , , AC_MSG_ERROR([GNU TLS development headers are needed.])) + GNUTLSFLAGS=`pkg-config --cflags gnutls` + GNUTLSLIBS=`pkg-config --libs gnutls` + LIBS="$LIBS $GNUTLSLIBS" + CPPFLAGS="$CPPFLAGS $GNUTLSFLAGS" + AC_DEFINE([HAVE_GNUTLS], [1], [If cryptography will be supplied by GNU TLS]) + HAVE_GNUTLS=1 + else + HAVE_GNUTLS=0 + AC_MSG_ERROR([pkg-config is needed to configure compilation with GNU TLS.]) + fi + else + HAVE_GNUTLS=0 + fi], + [HAVE_GNUTLS=0]) +if test "$HAVE_GNUTLS" = "1" ; then + AC_MSG_RESULT(yes) +else + AC_MSG_RESULT(no) +fi + AC_ARG_WITH(docbook-xsl, AS_HELP_STRING([--with-docbook-xsl=DIR], [location of docbook 4.x xml stylesheets]), diff --git a/libqpdf/MD5-gnutls.cc b/libqpdf/MD5-gnutls.cc new file mode 100644 index 0000000..5611b3e --- /dev/null +++ b/libqpdf/MD5-gnutls.cc @@ -0,0 +1,61 @@ +#include +#include +#include + + +// MD5 initialization. Begins an MD5 operation, writing a new context. +void MD5::init() +{ + int ret; + + ret = gnutls_hash_init(&ctx, GNUTLS_DIG_MD5); + if (ret < 0) { + QUtil::throw_system_error( + std::string("GNU TLS: MD5 error:") + std::string(gnutls_strerror(ret))); + ctx = nullptr; + } + + finalized = false; + memset(digest_val, 0, sizeof(digest_val)); +} + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block, and updating the +// context. + +void MD5::update(unsigned char *input, + unsigned int inputLen) +{ + if (ctx != nullptr && input != nullptr) + gnutls_hash(ctx, input, inputLen); +} + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. +void MD5::final() +{ + if (finalized) + return; + + if (ctx != nullptr) + { + gnutls_hash_deinit(ctx, digest_val); + ctx = nullptr; + } + + finalized = true; +} + +// MD5 basic transformation. Transforms state based on block. +void MD5::transform(UINT4 state[4], unsigned char block[64]) +{} + +// Encodes input (UINT4) into output (unsigned char). Assumes len is a +// multiple of 4. +void MD5::encode(unsigned char *output, UINT4 *input, unsigned int len) +{} + +// Decodes input (unsigned char) into output (UINT4). Assumes len is a +// multiple of 4. +void MD5::decode(UINT4 *output, unsigned char *input, unsigned int len) +{} diff --git a/libqpdf/MD5.cc b/libqpdf/MD5.cc index 0504e2d..19dce79 100644 --- a/libqpdf/MD5.cc +++ b/libqpdf/MD5.cc @@ -36,6 +36,10 @@ #include #include +#ifdef HAVE_GNUTLS +# include "MD5-gnutls.cc" +#else + int const S11 = 7; int const S12 = 12; int const S13 = 17; @@ -294,6 +298,8 @@ void MD5::decode(UINT4 *output, unsigned char *input, unsigned int len) (static_cast(input[j+3]) << 24); } +#endif /* HAVE_GNUTLS */ + // Public functions MD5::MD5() diff --git a/libqpdf/Pl_AES_PDF-gnutls.cc b/libqpdf/Pl_AES_PDF-gnutls.cc new file mode 100644 index 0000000..7704e6d --- /dev/null +++ b/libqpdf/Pl_AES_PDF-gnutls.cc @@ -0,0 +1,246 @@ +#include +#include +#include + +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, + unsigned int key_bytes) : + Pipeline(identifier, next), + encrypt(encrypt), + cbc_mode(true), + first(true), + offset(0), + nrounds(0), + use_zero_iv(false), + use_specified_iv(false), + disable_padding(false) +{ + unsigned int keybits = 8 * key_bytes; + this->keylen = key_bytes; + this->ctx = nullptr; + 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); +} + +Pl_AES_PDF::~Pl_AES_PDF() +{ + 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 +Pl_AES_PDF::setIV(unsigned char const* iv, size_t bytes) +{ + if (bytes != this->buf_size) + { + throw std::logic_error( + "Pl_AES_PDF: specified initialization vector" + " size in bytes must be " + QUtil::int_to_string(bytes)); + } + this->use_specified_iv = true; + memcpy(this->specified_iv, iv, bytes); +} + +void +Pl_AES_PDF::disableCBC() +{ + this->cbc_mode = false; +} + +void +Pl_AES_PDF::useStaticIV() +{ + use_static_iv = true; +} + +void +Pl_AES_PDF::write(unsigned char* data, size_t len) +{ + size_t bytes_left = len; + unsigned char* p = data; + + while (bytes_left > 0) + { + if (this->offset == this->buf_size) + { + flush(false); + } + + size_t available = this->buf_size - this->offset; + size_t bytes = (bytes_left < available ? bytes_left : available); + bytes_left -= bytes; + std::memcpy(this->inbuf + this->offset, p, bytes); + this->offset += bytes; + p += bytes; + } +} + +void +Pl_AES_PDF::finish() +{ + if (this->encrypt) + { + if (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 = + static_cast(this->buf_size - this->offset); + memset(this->inbuf + this->offset, pad, pad); + this->offset = this->buf_size; + flush(false); + } + } + else + { + if (this->offset != this->buf_size) + { + // This is never supposed to happen as the output is + // always supposed to be padded. However, we have + // encountered files for which the output is not a + // multiple of the block size. In this case, pad with + // zeroes and hope for the best. + assert(this->buf_size > this->offset); + std::memset(this->inbuf + this->offset, 0, + this->buf_size - this->offset); + this->offset = this->buf_size; + } + flush(! this->disable_padding); + } + + if (this->cbc_mode) + rijndaelFinish(this->ctx); + + getNext()->finish(); +} + +void +Pl_AES_PDF::initializeVector() +{ + if (use_zero_iv) + { + for (unsigned int i = 0; i < this->buf_size; ++i) + { + this->cbc_block[i] = 0; + } + } + else if (use_specified_iv) + { + std::memcpy(this->cbc_block, this->specified_iv, this->buf_size); + } + else if (use_static_iv) + { + for (unsigned int i = 0; i < this->buf_size; ++i) + { + this->cbc_block[i] = 14 * (1 + i); + } + } + else + { + QUtil::initializeWithRandomBytes(this->cbc_block, this->buf_size); + } +} + +void +Pl_AES_PDF::flush(bool strip_padding) +{ + assert(this->offset == this->buf_size); + + if (first) + { + first = false; + if (this->cbc_mode) + { + if (encrypt) + { + // Set cbc_block to the initialization vector, and if + // not zero, write it to the output stream. + initializeVector(); + if (! (this->use_zero_iv || this->use_specified_iv)) + { + getNext()->write(this->cbc_block, this->buf_size); + } + } + else if (this->use_zero_iv || this->use_specified_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. + memcpy(this->cbc_block, this->inbuf, this->buf_size); + this->offset = 0; + rijndaelInit(&(this->ctx), this->key, this->keylen, this->cbc_block, this->buf_size); + return; + } + rijndaelInit(&(this->ctx), this->key, this->keylen, this->cbc_block, this->buf_size); + } + } + + if (this->encrypt) + { + if (this->cbc_mode) + rijndaelCBCEncrypt(this->ctx, this->inbuf, this->outbuf, this->buf_size); + else + rijndaelECBEncrypt(this->key, this->keylen, this->cbc_block, this->inbuf, this->outbuf, this->buf_size); + } + else + { + if (this->cbc_mode) + rijndaelCBCDecrypt(this->ctx, this->inbuf, this->outbuf, this->buf_size); + else + rijndaelECBDecrypt(this->key, this->keylen, this->cbc_block, this->inbuf, this->outbuf, this->buf_size); + } + unsigned int bytes = this->buf_size; + if (strip_padding) + { + unsigned char last = this->outbuf[this->buf_size - 1]; + if (last <= this->buf_size) + { + bool strip = true; + for (unsigned int i = 1; i <= last; ++i) + { + if (this->outbuf[this->buf_size - i] != last) + { + strip = false; + break; + } + } + if (strip) + { + bytes -= last; + } + } + } + getNext()->write(this->outbuf, bytes); + this->offset = 0; +} diff --git a/libqpdf/Pl_AES_PDF.cc b/libqpdf/Pl_AES_PDF.cc index 5c493cb..ac8bde4 100644 --- a/libqpdf/Pl_AES_PDF.cc +++ b/libqpdf/Pl_AES_PDF.cc @@ -3,10 +3,15 @@ #include #include #include -#include + #include #include +#ifdef HAVE_GNUTLS +# include "Pl_AES_PDF-gnutls.cc" +#else +# include + bool Pl_AES_PDF::use_static_iv = false; Pl_AES_PDF::Pl_AES_PDF(char const* identifier, Pipeline* next, @@ -263,3 +268,4 @@ Pl_AES_PDF::flush(bool strip_padding) getNext()->write(this->outbuf, bytes); this->offset = 0; } +#endif diff --git a/libqpdf/Pl_SHA2.cc b/libqpdf/Pl_SHA2.cc index 17eff7e..9156451 100644 --- a/libqpdf/Pl_SHA2.cc +++ b/libqpdf/Pl_SHA2.cc @@ -13,6 +13,12 @@ Pl_SHA2::Pl_SHA2(int bits, Pipeline* next) : { resetBits(bits); } + +#ifdef HAVE_GNUTLS + this->ctx256 = nullptr; + this->ctx384 = nullptr; + this->ctx512 = nullptr; +#endif } Pl_SHA2::~Pl_SHA2() diff --git a/libqpdf/RC4-gnutls.cc b/libqpdf/RC4-gnutls.cc new file mode 100644 index 0000000..093a5b9 --- /dev/null +++ b/libqpdf/RC4-gnutls.cc @@ -0,0 +1,51 @@ +#include +#include +#include + + +RC4::RC4(unsigned char const* key_data, int key_len) +{ + if (key_len == -1) + { + key_len = strlen(reinterpret_cast(key_data)); + } + + int ret; + gnutls_cipher_algorithm_t alg = GNUTLS_CIPHER_ARCFOUR_128; + + this->key.data = const_cast(key_data); + this->key.size = key_len; + + ret = gnutls_cipher_init(&(this->ctx), + alg, + &(this->key), + NULL); + if (ret < 0) + { + QUtil::throw_system_error( + std::string("GNU TLS: RC4 error: ") + std::string(gnutls_strerror(ret))); + this->ctx = nullptr; + } +} + +RC4::~RC4() +{ + if (this->ctx != nullptr) + { + gnutls_cipher_deinit(this->ctx); + this->ctx = nullptr; + } +} + +void +RC4::process(unsigned char *in_data, int len, unsigned char* out_data) +{ + if (out_data == 0) + { + // Convert in place + out_data = in_data; + } + + if (this->ctx != nullptr && in_data != nullptr) + gnutls_cipher_encrypt2(this->ctx, in_data, len, out_data, len); +} diff --git a/libqpdf/RC4.cc b/libqpdf/RC4.cc index 7639a36..c1288be 100644 --- a/libqpdf/RC4.cc +++ b/libqpdf/RC4.cc @@ -2,6 +2,10 @@ #include +#ifdef HAVE_GNUTLS +# include "RC4-gnutls.cc" +#else + static void swap_byte(unsigned char &a, unsigned char &b) { unsigned char t; @@ -53,3 +57,4 @@ RC4::process(unsigned char *in_data, int len, unsigned char* out_data) out_data[i] = in_data[i] ^ key.state[xor_index]; } } +#endif diff --git a/libqpdf/SecureRandomDataProvider.cc b/libqpdf/SecureRandomDataProvider.cc index fe9f1d4..d72269e 100644 --- a/libqpdf/SecureRandomDataProvider.cc +++ b/libqpdf/SecureRandomDataProvider.cc @@ -11,6 +11,10 @@ # endif #endif +#ifdef HAVE_GNUTLS +# include +#endif + SecureRandomDataProvider::SecureRandomDataProvider() { } @@ -89,7 +93,19 @@ class WindowsCryptProvider void SecureRandomDataProvider::provideRandomData(unsigned char* data, size_t len) { -#if defined(_WIN32) +#if defined(HAVE_GNUTLS) + + int ret = 0; + + ret = gnutls_rnd(GNUTLS_RND_NONCE, data, len); + if (ret < 0) + { + throw std::runtime_error( + "GNU TLS: unable to generate secure random data " + + std::string(gnutls_strerror(ret))); + } + +#elif defined(_WIN32) // Optimization: make the WindowsCryptProvider static as long as // it can be done in a thread-safe fashion. diff --git a/libqpdf/qpdf/MD5.hh b/libqpdf/qpdf/MD5.hh index 4cfe027..bb9f756 100644 --- a/libqpdf/qpdf/MD5.hh +++ b/libqpdf/qpdf/MD5.hh @@ -11,6 +11,10 @@ #endif #include +#ifdef HAVE_GNUTLS +# include +#endif + class MD5 { public: @@ -87,6 +91,9 @@ class MD5 bool finalized; Digest digest_val; +#ifdef HAVE_GNUTLS + gnutls_hash_hd_t ctx; +#endif }; #endif // __MD5_HH__ diff --git a/libqpdf/qpdf/Pl_AES_PDF.hh b/libqpdf/qpdf/Pl_AES_PDF.hh index 9aa73ad..8059e7d 100644 --- a/libqpdf/qpdf/Pl_AES_PDF.hh +++ b/libqpdf/qpdf/Pl_AES_PDF.hh @@ -7,6 +7,10 @@ # include #endif +#ifdef HAVE_GNUTLS +# include +#endif + // This pipeline implements AES-128 and AES-256 with CBC and block // padding as specified in the PDF specification. @@ -64,6 +68,11 @@ class Pl_AES_PDF: public Pipeline bool use_zero_iv; bool use_specified_iv; bool disable_padding; + +#ifdef HAVE_GNUTLS + gnutls_cipher_hd_t ctx; + unsigned int keylen; +#endif }; #endif // __PL_AES_PDF_HH__ diff --git a/libqpdf/qpdf/RC4.hh b/libqpdf/qpdf/RC4.hh index c26f3d0..1421a08 100644 --- a/libqpdf/qpdf/RC4.hh +++ b/libqpdf/qpdf/RC4.hh @@ -1,6 +1,12 @@ #ifndef __RC4_HH__ #define __RC4_HH__ +#include + +#ifdef HAVE_GNUTLS +# include +#endif + class RC4 { public: @@ -10,7 +16,15 @@ class RC4 // out_data = 0 means to encrypt/decrypt in place void process(unsigned char* in_data, int len, unsigned char* out_data = 0); +#ifdef HAVE_GNUTLS + ~RC4(); +#endif + private: +#ifdef HAVE_GNUTLS + gnutls_cipher_hd_t ctx; + gnutls_datum_t key; +#else class RC4Key { public: @@ -20,6 +34,7 @@ class RC4 }; RC4Key key; +#endif }; #endif // __RC4_HH__ diff --git a/libqpdf/qpdf/rijndael-gnutls.h b/libqpdf/qpdf/rijndael-gnutls.h new file mode 100644 index 0000000..a3e7ff0 --- /dev/null +++ b/libqpdf/qpdf/rijndael-gnutls.h @@ -0,0 +1,32 @@ +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif + +#include + +void rijndaelInit(gnutls_cipher_hd_t * context, unsigned char * key, + unsigned int keylen, unsigned char cbc_block[16], unsigned int buf_size); +int rijndaelSetupEncrypt(uint32_t *rk, const unsigned char *key, + int keybits); +int rijndaelSetupDecrypt(uint32_t *rk, const unsigned char *key, + int keybits); +void rijndaelCBCEncrypt(gnutls_cipher_hd_t context, + const unsigned char plaintext[16], unsigned char ciphertext[16], + unsigned int buf_size); +void rijndaelCBCDecrypt(gnutls_cipher_hd_t context, + const unsigned char ciphertext[16], unsigned char plaintext[16], + unsigned int buf_size); +void rijndaelECBEncrypt(unsigned char * key, unsigned int keylen, + unsigned char cbc_block[16], const unsigned char plaintext[16], + unsigned char ciphertext[16], unsigned int buf_size); +void rijndaelECBDecrypt(unsigned char * key, unsigned int keylen, + unsigned char cbc_block[16], const unsigned char ciphertext[16], + unsigned char plaintext[16], unsigned int buf_size); +void rijndaelFinish(gnutls_cipher_hd_t context); + +#define KEYLENGTH(keybits) ((keybits)/8) +#define RKLENGTH(keybits) ((keybits)/8+28) +#define NROUNDS(keybits) ((keybits)/32+6) diff --git a/libqpdf/qpdf/rijndael.h b/libqpdf/qpdf/rijndael.h index a9cd71d..9698a8b 100644 --- a/libqpdf/qpdf/rijndael.h +++ b/libqpdf/qpdf/rijndael.h @@ -9,6 +9,10 @@ # include #endif +#ifdef HAVE_GNUTLS +# include "qpdf/rijndael-gnutls.h" +#else + int rijndaelSetupEncrypt(uint32_t *rk, const unsigned char *key, int keybits); int rijndaelSetupDecrypt(uint32_t *rk, const unsigned char *key, @@ -22,4 +26,6 @@ void rijndaelDecrypt(const uint32_t *rk, int nrounds, #define RKLENGTH(keybits) ((keybits)/8+28) #define NROUNDS(keybits) ((keybits)/32+6) +#endif /* HAVE_GNUTLS */ + #endif diff --git a/libqpdf/rijndael-gnutls.cc b/libqpdf/rijndael-gnutls.cc new file mode 100644 index 0000000..dd3fdb2 --- /dev/null +++ b/libqpdf/rijndael-gnutls.cc @@ -0,0 +1,116 @@ +#include +#include +#include +#include + +typedef uint32_t u32; + +/** + * Init cryptographic context + */ +void rijndaelInit(gnutls_cipher_hd_t * ctx, unsigned char * key, + unsigned int keylen, unsigned char cbc_block[16], + unsigned int buf_size) +{ + int ret; + gnutls_cipher_algorithm_t alg; + gnutls_datum_t cipher_key; + gnutls_datum_t iv; + + cipher_key.data = key; + + switch(keylen) { + case 16: + alg = GNUTLS_CIPHER_AES_128_CBC; + break; + case 32: + alg = GNUTLS_CIPHER_AES_256_CBC; + break; + case 24: + alg = GNUTLS_CIPHER_AES_192_CBC; + break; + default: + alg = GNUTLS_CIPHER_AES_128_CBC; + break; + } + + cipher_key.size = gnutls_cipher_get_key_size(alg); + + iv.data = cbc_block; + iv.size = buf_size; + + ret = gnutls_cipher_init(ctx, alg, &cipher_key, &iv); + if (ret < 0) + { + QUtil::throw_system_error( + std::string("GNU TLS: AES error: ") + std::string(gnutls_strerror(ret))); + ctx = NULL; + return; + } +} + +/** + * Encrypt string by AES CBC by GNU TLS. + */ +void rijndaelCBCEncrypt(gnutls_cipher_hd_t ctx, + const unsigned char plaintext[16], unsigned char ciphertext[16], + unsigned int buf_size) +{ + if (ctx != nullptr) + gnutls_cipher_encrypt2(ctx, plaintext, buf_size, ciphertext, buf_size); +} + +/** + * Decrypt string by AES CBC by GNU TLS. + */ +void rijndaelCBCDecrypt(gnutls_cipher_hd_t ctx, + const unsigned char ciphertext[16], unsigned char plaintext[16], + unsigned int buf_size) +{ + if (ctx != nullptr) + gnutls_cipher_decrypt2(ctx, ciphertext, buf_size, plaintext, buf_size); +} + +/** + * Encrypt string by AES ECB by GNU TLS. + */ +void rijndaelECBEncrypt(unsigned char * key, unsigned int keylen, + unsigned char cbc_block[16], const unsigned char ciphertext[16], + unsigned char plaintext[16], unsigned int buf_size) +{ + gnutls_cipher_hd_t ctx; + + rijndaelInit(&ctx, key, keylen, cbc_block, buf_size); + + rijndaelCBCEncrypt(ctx, ciphertext, plaintext, buf_size); + + rijndaelFinish(ctx); +} + +/** + * Decrypt string by AES ECB by GNU TLS. + */ +void rijndaelECBDecrypt(unsigned char * key, unsigned int keylen, + unsigned char cbc_block[16], const unsigned char ciphertext[16], + unsigned char plaintext[16], unsigned int buf_size) +{ + gnutls_cipher_hd_t ctx; + + rijndaelInit(&ctx, key, keylen, cbc_block, buf_size); + + rijndaelCBCDecrypt(ctx, ciphertext, plaintext, buf_size); + + rijndaelFinish(ctx); +} + +/** + * Finish cryptography context + */ +void rijndaelFinish(gnutls_cipher_hd_t ctx) +{ + if (ctx != nullptr) + { + gnutls_cipher_deinit(ctx); + ctx = nullptr; + } +} diff --git a/libqpdf/rijndael.cc b/libqpdf/rijndael.cc index 7f711df..117c1a1 100644 --- a/libqpdf/rijndael.cc +++ b/libqpdf/rijndael.cc @@ -2,6 +2,10 @@ #include "qpdf/rijndael.h" +#ifdef HAVE_GNUTLS +# include "rijndael-gnutls.cc" +#else + typedef uint32_t u32; typedef unsigned char u8; @@ -1206,3 +1210,4 @@ void rijndaelDecrypt(const u32 *rk, int nrounds, const u8 ciphertext[16], rk[3]; PUTU32(plaintext + 12, s3); } +#endif diff --git a/libqpdf/sha2-gnutls.c b/libqpdf/sha2-gnutls.c new file mode 100644 index 0000000..29155d5 --- /dev/null +++ b/libqpdf/sha2-gnutls.c @@ -0,0 +1,41 @@ +#include +#include +#include "sph/sph_sha2_gnutls.h" + + +/* see sph_sha2.h */ +void +sph_sha256_init(void * cc) +{ + int ret; + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + ret = gnutls_hash_init(ctx, GNUTLS_DIG_SHA256); + if (ret < 0) + { + fprintf(stderr, "GNU TLS: SHA256 error : %s\n", gnutls_strerror(ret)); + cc = NULL; + } +} + +/* see sph_sha2.h */ +void +sph_sha256_close(void * cc, void *dst) +{ + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + if (ctx != NULL) + { + gnutls_hash_deinit(*(ctx), dst); + cc = NULL; + } +} + +/* see sph_sha2.h */ +void sph_sha256(void * cc, const void * data, size_t len) +{ + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + if (ctx != NULL && data != NULL) + gnutls_hash(*(ctx), data, len); +} diff --git a/libqpdf/sha2.c b/libqpdf/sha2.c index 45fdd7e..e240f9b 100644 --- a/libqpdf/sha2.c +++ b/libqpdf/sha2.c @@ -35,6 +35,10 @@ #include "sph/sph_sha2.h" +#ifdef HAVE_GNUTLS +# include "sha2-gnutls.c" +#else + #if SPH_SMALL_FOOTPRINT && !defined SPH_SMALL_FOOTPRINT_SHA2 #define SPH_SMALL_FOOTPRINT_SHA2 1 #endif @@ -688,3 +692,5 @@ sph_sha224_comp(const sph_u32 msg[16], sph_u32 val[8]) SHA2_ROUND_BODY(SHA2_IN, val); #undef SHA2_IN } + +#endif /* HAVE_GNUTLS */ diff --git a/libqpdf/sha2big-gnutls.c b/libqpdf/sha2big-gnutls.c new file mode 100644 index 0000000..494e2b4 --- /dev/null +++ b/libqpdf/sha2big-gnutls.c @@ -0,0 +1,80 @@ +#include +#include +#include "sph/sph_sha2_gnutls.h" + + +/* see sph_sha3.h */ +void +sph_sha384_init(void * cc) +{ + int ret; + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + ret = gnutls_hash_init(ctx, GNUTLS_DIG_SHA384); + if (ret < 0) + { + fprintf(stderr, "GNU TLS: SHA384 error: %s\n", gnutls_strerror(ret)); + cc = NULL; + } +} + +/* see sph_sha3.h */ +void +sph_sha384_close(void * cc, void *dst) +{ + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + if (ctx != NULL && dst != NULL) + { + gnutls_hash_deinit(*(ctx), dst); + cc = NULL; + } +} + +/* see sph_sha3.h */ +void +sph_sha384(void * cc, const void * data, size_t len) +{ + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + if (ctx != NULL && data != NULL) + gnutls_hash(*(ctx), data, len); +} + +/* see sph_sha3.h */ +void +sph_sha512_init(void * cc) +{ + int ret; + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + ret = gnutls_hash_init(ctx, GNUTLS_DIG_SHA512); + if (ret < 0) + { + fprintf(stderr, "GNU TLS: SHA512 error: %s\n", gnutls_strerror(ret)); + cc = NULL; + } +} + +/* see sph_sha3.h */ +void +sph_sha512_close(void * cc, void * dst) +{ + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + if (ctx != NULL && dst != NULL) + { + gnutls_hash_deinit(*(ctx), dst); + cc = NULL; + } +} + +/* see sph_sha3.h */ +void +sph_sha512(void * cc, const void * data, size_t len) +{ + gnutls_hash_hd_t * ctx = (gnutls_hash_hd_t*) cc; + + if (ctx != NULL && data != NULL) + gnutls_hash(*(ctx), data, len); +} diff --git a/libqpdf/sha2big.c b/libqpdf/sha2big.c index e4aadbd..3194a10 100644 --- a/libqpdf/sha2big.c +++ b/libqpdf/sha2big.c @@ -35,6 +35,10 @@ #include "sph/sph_sha2.h" +#ifdef HAVE_GNUTLS +# include "sha2big-gnutls.c" +#else + #if SPH_64 #define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) @@ -245,3 +249,5 @@ sph_sha384_comp(const sph_u64 msg[16], sph_u64 val[8]) } #endif + +#endif /* HAVE_GNUTLS */ diff --git a/libqpdf/sph/sph_sha2.h b/libqpdf/sph/sph_sha2.h index 4bff9cd..584c6ab 100644 --- a/libqpdf/sph/sph_sha2.h +++ b/libqpdf/sph/sph_sha2.h @@ -47,6 +47,12 @@ extern "C" { #include #include "sph_types.h" +#include + +#ifdef HAVE_GNUTLS +# include "sph_sha2_gnutls.h" +#else + /** * Output size (in bits) for SHA-224. */ @@ -371,6 +377,8 @@ void sph_sha512_comp(const sph_u64 msg[16], sph_u64 val[8]); #endif +#endif /* HAVE_GNUTLS */ + #ifdef __cplusplus } #endif diff --git a/libqpdf/sph/sph_sha2_gnutls.h b/libqpdf/sph/sph_sha2_gnutls.h new file mode 100644 index 0000000..1fb26a4 --- /dev/null +++ b/libqpdf/sph/sph_sha2_gnutls.h @@ -0,0 +1,88 @@ +#include +#include + +typedef gnutls_hash_hd_t sph_sha256_context; +typedef gnutls_hash_hd_t sph_sha384_context; +typedef gnutls_hash_hd_t sph_sha512_context; + +/** + * Initialize a SHA-256 context. + * + * @param cc the SHA-256 context (pointer to + * a sph_sha256_context) + */ +void sph_sha256_init(void *cc); + +/** + * Process some data bytes, for SHA-256. + * + * @param cc the SHA-224 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha256(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-256 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (32 bytes). + * + * @param cc the SHA-256 context + * @param dst the destination buffer + */ +void sph_sha256_close(void *cc, void *dst); + +/** + * Initialize a SHA-384 context. + * + * @param cc the SHA-384 context (pointer to + * a sph_sha384_context) + */ +void sph_sha384_init(void *cc); + +/** + * Process some data bytes. It is acceptable that len is zero + * (in which case this function does nothing). + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha384(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-384 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (48 bytes). + * + * @param cc the SHA-384 context + * @param dst the destination buffer + */ +void sph_sha384_close(void *cc, void *dst); + +/** + * Initialize a SHA-512 context. + * + * @param cc the SHA-512 context (pointer to + * a sph_sha512_context) + */ +void sph_sha512_init(void *cc); + +/** + * Process some data bytes, for SHA-512. + * + * @param cc the SHA-384 context + * @param data the input data + * @param len the input data length (in bytes) + */ +void sph_sha512(void *cc, const void *data, size_t len); + +/** + * Terminate the current SHA-512 computation and output the result into the + * provided buffer. The destination buffer must be wide enough to + * accomodate the result (64 bytes). + * + * @param cc the SHA-512 context + * @param dst the destination buffer + */ +void sph_sha512_close(void *cc, void *dst); diff --git a/libqpdf/sph/sph_types.h b/libqpdf/sph/sph_types.h index eab09a2..36d1ff6 100644 --- a/libqpdf/sph/sph_types.h +++ b/libqpdf/sph/sph_types.h @@ -48,6 +48,7 @@ #define SPH_TYPES_H__ #include +#include /* * All our I/O functions are defined over octet streams. We do not know