aboutsummaryrefslogtreecommitdiffstats
path: root/libqpdf
diff options
context:
space:
mode:
authorDean Scarff <deanscarff@google.com>2020-04-06 05:19:56 +0200
committerJay Berkenbilt <jberkenbilt@users.noreply.github.com>2020-04-06 15:01:55 +0200
commit0f2507234fbe3bd305404b1267607b9900857523 (patch)
tree9484ca1d93d3def6125f94b512b6c5b8e390c9a4 /libqpdf
parent08379321645133ba74dad4ce130055c087130bab (diff)
downloadqpdf-0f2507234fbe3bd305404b1267607b9900857523.tar.zst
Add OpenSSL/BoringSSL crypto provider
Fixes qpdf/qpdf#417
Diffstat (limited to 'libqpdf')
-rw-r--r--libqpdf/QPDFCryptoProvider.cc6
-rw-r--r--libqpdf/QPDFCrypto_openssl.cc193
-rw-r--r--libqpdf/build.mk7
-rw-r--r--libqpdf/qpdf/QPDFCrypto_openssl.hh50
-rw-r--r--libqpdf/qpdf/qpdf-config.h.in3
5 files changed, 259 insertions, 0 deletions
diff --git a/libqpdf/QPDFCryptoProvider.cc b/libqpdf/QPDFCryptoProvider.cc
index 5755fcca..b375498d 100644
--- a/libqpdf/QPDFCryptoProvider.cc
+++ b/libqpdf/QPDFCryptoProvider.cc
@@ -9,6 +9,9 @@
#ifdef USE_CRYPTO_GNUTLS
# include <qpdf/QPDFCrypto_gnutls.hh>
#endif
+#ifdef USE_CRYPTO_OPENSSL
+# include <qpdf/QPDFCrypto_openssl.hh>
+#endif
std::shared_ptr<QPDFCryptoImpl>
QPDFCryptoProvider::getImpl()
@@ -50,6 +53,9 @@ QPDFCryptoProvider::QPDFCryptoProvider() :
#ifdef USE_CRYPTO_GNUTLS
registerImpl_internal<QPDFCrypto_gnutls>("gnutls");
#endif
+#ifdef USE_CRYPTO_OPENSSL
+ registerImpl_internal<QPDFCrypto_openssl>("openssl");
+#endif
std::string default_crypto;
if (! QUtil::get_env("QPDF_CRYPTO_PROVIDER", &default_crypto))
{
diff --git a/libqpdf/QPDFCrypto_openssl.cc b/libqpdf/QPDFCrypto_openssl.cc
new file mode 100644
index 00000000..c226fc9c
--- /dev/null
+++ b/libqpdf/QPDFCrypto_openssl.cc
@@ -0,0 +1,193 @@
+#include <qpdf/QPDFCrypto_openssl.hh>
+
+#include <cstring>
+#include <stdexcept>
+
+#include <qpdf/QIntC.hh>
+
+
+static void
+bad_bits(int bits)
+{
+ throw std::logic_error(
+ std::string("unsupported key length: ") + std::to_string(bits));
+}
+
+static void
+check_openssl(int status)
+{
+ if (status != 1)
+ {
+ throw std::runtime_error("openssl error");
+ }
+}
+
+QPDFCrypto_openssl::QPDFCrypto_openssl() :
+ md_ctx(EVP_MD_CTX_new()), cipher_ctx(EVP_CIPHER_CTX_new())
+{
+ memset(md_out, 0, sizeof(md_out));
+ EVP_MD_CTX_init(md_ctx);
+ EVP_CIPHER_CTX_init(cipher_ctx);
+}
+
+QPDFCrypto_openssl::~QPDFCrypto_openssl()
+{
+ EVP_MD_CTX_reset(md_ctx);
+ EVP_CIPHER_CTX_reset(cipher_ctx);
+ EVP_CIPHER_CTX_free(cipher_ctx);
+ EVP_MD_CTX_free(md_ctx);
+}
+
+void
+QPDFCrypto_openssl::MD5_init()
+{
+ check_openssl(EVP_MD_CTX_reset(md_ctx));
+ check_openssl(EVP_DigestInit_ex(md_ctx, EVP_md5(), nullptr));
+}
+
+void
+QPDFCrypto_openssl::SHA2_init(int bits)
+{
+ const EVP_MD* md = EVP_sha512();
+ switch (bits)
+ {
+ case 256:
+ md = EVP_sha256();
+ break;
+ case 384:
+ md = EVP_sha384();
+ break;
+ case 512:
+ md = EVP_sha512();
+ break;
+ default:
+ bad_bits(bits);
+ return;
+ }
+ sha2_bits = static_cast<size_t>(bits);
+ check_openssl(EVP_MD_CTX_reset(md_ctx));
+ check_openssl(EVP_DigestInit_ex(md_ctx, md, nullptr));
+}
+
+void
+QPDFCrypto_openssl::MD5_update(unsigned char const* data, size_t len)
+{
+ check_openssl(EVP_DigestUpdate(md_ctx, data, len));
+}
+
+void
+QPDFCrypto_openssl::SHA2_update(unsigned char const* data, size_t len)
+{
+ check_openssl(EVP_DigestUpdate(md_ctx, data, len));
+}
+
+void
+QPDFCrypto_openssl::MD5_finalize()
+{
+ if (EVP_MD_CTX_md(md_ctx))
+ {
+ check_openssl(EVP_DigestFinal(md_ctx, md_out + 0, nullptr));
+ }
+}
+
+void
+QPDFCrypto_openssl::SHA2_finalize()
+{
+ if (EVP_MD_CTX_md(md_ctx))
+ {
+ check_openssl(EVP_DigestFinal(md_ctx, md_out + 0, nullptr));
+ }
+}
+
+void
+QPDFCrypto_openssl::MD5_digest(MD5_Digest d)
+{
+ memcpy(d, md_out, sizeof(QPDFCryptoImpl::MD5_Digest));
+}
+
+std::string
+QPDFCrypto_openssl::SHA2_digest()
+{
+ return std::string(reinterpret_cast<char*>(md_out), sha2_bits / 8);
+}
+
+void
+QPDFCrypto_openssl::RC4_init(unsigned char const* key_data, int key_len)
+{
+ check_openssl(EVP_CIPHER_CTX_reset(cipher_ctx));
+ if (key_len == -1)
+ {
+ key_len = QIntC::to_int(
+ strlen(reinterpret_cast<const char*>(key_data)));
+ }
+ check_openssl(
+ EVP_EncryptInit_ex(cipher_ctx, EVP_rc4(), nullptr, nullptr, nullptr));
+ check_openssl(EVP_CIPHER_CTX_set_key_length(cipher_ctx, key_len));
+ check_openssl(
+ EVP_EncryptInit_ex(cipher_ctx, nullptr, nullptr, key_data, nullptr));
+}
+
+void
+QPDFCrypto_openssl::rijndael_init(
+ bool encrypt, unsigned char const* key_data, size_t key_len,
+ bool cbc_mode, unsigned char* cbc_block)
+{
+ const EVP_CIPHER* cipher = nullptr;
+ switch (key_len)
+ {
+ case 32:
+ cipher = cbc_mode ? EVP_aes_256_cbc() : EVP_aes_256_ecb();
+ break;
+ case 24:
+ cipher = cbc_mode ? EVP_aes_192_cbc() : EVP_aes_192_ecb();
+ break;
+ default:
+ cipher = cbc_mode ? EVP_aes_128_cbc() : EVP_aes_128_ecb();
+ break;
+ }
+
+ check_openssl(EVP_CIPHER_CTX_reset(cipher_ctx));
+ check_openssl(
+ EVP_CipherInit_ex(cipher_ctx, cipher, nullptr,
+ key_data, cbc_block, encrypt));
+ check_openssl(EVP_CIPHER_CTX_set_padding(cipher_ctx, 0));
+}
+
+void
+QPDFCrypto_openssl::RC4_process(
+ unsigned char* in_data, size_t len, unsigned char* out_data)
+{
+ if (nullptr == out_data)
+ {
+ out_data = in_data;
+ }
+ int out_len = static_cast<int>(len);
+ check_openssl(
+ EVP_EncryptUpdate(cipher_ctx, out_data, &out_len, in_data, out_len));
+}
+
+void
+QPDFCrypto_openssl::rijndael_process(
+ unsigned char* in_data, unsigned char* out_data)
+{
+ int len = QPDFCryptoImpl::rijndael_buf_size;
+ check_openssl(EVP_CipherUpdate(cipher_ctx, out_data, &len, in_data, len));
+}
+
+void
+QPDFCrypto_openssl::RC4_finalize()
+{
+ if (EVP_CIPHER_CTX_cipher(cipher_ctx))
+ {
+ check_openssl(EVP_CIPHER_CTX_reset(cipher_ctx));
+ }
+}
+
+void
+QPDFCrypto_openssl::rijndael_finalize()
+{
+ if (EVP_CIPHER_CTX_cipher(cipher_ctx))
+ {
+ check_openssl(EVP_CIPHER_CTX_reset(cipher_ctx));
+ }
+}
diff --git a/libqpdf/build.mk b/libqpdf/build.mk
index ac904174..69b1693c 100644
--- a/libqpdf/build.mk
+++ b/libqpdf/build.mk
@@ -14,6 +14,9 @@ CRYPTO_NATIVE = \
libqpdf/sha2.c \
libqpdf/sha2big.c
+CRYPTO_OPENSSL = \
+ libqpdf/QPDFCrypto_openssl.cc
+
CRYPTO_GNUTLS = \
libqpdf/QPDFCrypto_gnutls.cc
@@ -97,6 +100,10 @@ ifeq ($(USE_CRYPTO_NATIVE), 1)
SRCS_libqpdf += $(CRYPTO_NATIVE)
endif
+ifeq ($(USE_CRYPTO_OPENSSL), 1)
+SRCS_libqpdf += $(CRYPTO_OPENSSL)
+endif
+
ifeq ($(USE_CRYPTO_GNUTLS), 1)
SRCS_libqpdf += $(CRYPTO_GNUTLS)
endif
diff --git a/libqpdf/qpdf/QPDFCrypto_openssl.hh b/libqpdf/qpdf/QPDFCrypto_openssl.hh
new file mode 100644
index 00000000..ff8c4831
--- /dev/null
+++ b/libqpdf/qpdf/QPDFCrypto_openssl.hh
@@ -0,0 +1,50 @@
+#ifndef QPDFCRYPTO_openssl_HH
+#define QPDFCRYPTO_openssl_HH
+
+#include <qpdf/QPDFCryptoImpl.hh>
+#include <string>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/cipher.h>
+#include <openssl/digest.h>
+#else
+#include <openssl/evp.h>
+#endif
+
+class QPDFCrypto_openssl: public QPDFCryptoImpl
+{
+ public:
+ QPDFCrypto_openssl();
+
+ QPDF_DLL
+ ~QPDFCrypto_openssl() override;
+
+ void MD5_init() override;
+ void MD5_update(unsigned char const* data, size_t len) override;
+ void MD5_finalize() override;
+ void MD5_digest(MD5_Digest) override;
+
+ void RC4_init(unsigned char const* key_data, int key_len = -1) override;
+ void RC4_process(unsigned char* in_data, size_t len,
+ unsigned char* out_data = 0) override;
+ void RC4_finalize() override;
+
+ void SHA2_init(int bits) override;
+ void SHA2_update(unsigned char const* data, size_t len) override;
+ void SHA2_finalize() override;
+ std::string SHA2_digest() override;
+
+ void rijndael_init(
+ bool encrypt, unsigned char const* key_data, size_t key_len,
+ bool cbc_mode, unsigned char* cbc_block) override;
+ void rijndael_process(
+ unsigned char* in_data, unsigned char* out_data) override;
+ void rijndael_finalize() override;
+
+ private:
+ EVP_MD_CTX* const md_ctx;
+ EVP_CIPHER_CTX* const cipher_ctx;
+ uint8_t md_out[EVP_MAX_MD_SIZE];
+ size_t sha2_bits;
+};
+
+#endif // QPDFCRYPTO_openssl_HH
diff --git a/libqpdf/qpdf/qpdf-config.h.in b/libqpdf/qpdf/qpdf-config.h.in
index 75d34c66..c9886b57 100644
--- a/libqpdf/qpdf/qpdf-config.h.in
+++ b/libqpdf/qpdf/qpdf-config.h.in
@@ -90,6 +90,9 @@
/* Whether to use the native crypto provider */
#undef USE_CRYPTO_NATIVE
+/* Whether to use the openssl crypto provider */
+#undef USE_CRYPTO_OPENSSL
+
/* Whether to use insecure random numbers */
#undef USE_INSECURE_RANDOM