From ac67b05d19037606a10e523836768afbcf1531ae Mon Sep 17 00:00:00 2001 From: Jay Berkenbilt Date: Sun, 2 Mar 2025 19:10:06 -0500 Subject: [PATCH] Make it actually possible to register a crypto impl --- include/qpdf/QPDFCryptoProvider.hh | 17 +++++++++++++---- libqpdf/QPDFCryptoProvider.cc | 16 +++++++--------- libtests/CMakeLists.txt | 1 + libtests/crypto_provider.cc | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ libtests/qtest/crypto_provider.test | 16 ++++++++++++++++ 5 files changed, 135 insertions(+), 13 deletions(-) create mode 100644 libtests/crypto_provider.cc create mode 100644 libtests/qtest/crypto_provider.test diff --git a/include/qpdf/QPDFCryptoProvider.hh b/include/qpdf/QPDFCryptoProvider.hh index df4dda8..1f3e891 100644 --- a/include/qpdf/QPDFCryptoProvider.hh +++ b/include/qpdf/QPDFCryptoProvider.hh @@ -46,10 +46,21 @@ class QPDFCryptoProvider QPDF_DLL static std::shared_ptr getImpl(std::string const& name); + typedef std::function()> provider_fn; + + // Register a crypto implementation with the given name. The provider function must return + // a shared pointer to an instance of the implementation class, which must be derived from + // QPDFCryptoImpl. + QPDF_DLL static void registerImpl(std::string const& name, provider_fn f); + // Register the given type (T) as a crypto implementation. T must be derived from QPDFCryptoImpl // and must have a constructor that takes no arguments. template - QPDF_DLL static void registerImpl(std::string const& name); + static void + registerImpl(std::string const& name) + { + registerImpl(name, std::make_shared); + } // Set the crypto provider registered with the given name as the default crypto implementation. QPDF_DLL @@ -72,8 +83,7 @@ class QPDFCryptoProvider static QPDFCryptoProvider& getInstance(); std::shared_ptr getImpl_internal(std::string const& name) const; - template - void registerImpl_internal(std::string const& name); + void registerImpl_internal(std::string const& name, provider_fn f); void setDefaultProvider_internal(std::string const& name); class Members @@ -89,7 +99,6 @@ class QPDFCryptoProvider Members(Members const&) = delete; Members& operator=(Members const&) = delete; - typedef std::function()> provider_fn; std::string default_provider; std::map providers; }; diff --git a/libqpdf/QPDFCryptoProvider.cc b/libqpdf/QPDFCryptoProvider.cc index 2a18a36..89b5284 100644 --- a/libqpdf/QPDFCryptoProvider.cc +++ b/libqpdf/QPDFCryptoProvider.cc @@ -30,11 +30,10 @@ QPDFCryptoProvider::getImpl(std::string const& name) return getInstance().getImpl_internal(name); } -template void -QPDFCryptoProvider::registerImpl(std::string const& name) +QPDFCryptoProvider::registerImpl(std::string const& name, provider_fn f) { - getInstance().registerImpl_internal(name); + getInstance().registerImpl_internal(name, std::move(f)); } void @@ -47,13 +46,13 @@ QPDFCryptoProvider::QPDFCryptoProvider() : m(std::make_shared()) { #ifdef USE_CRYPTO_NATIVE - registerImpl_internal("native"); + registerImpl_internal("native", std::make_shared); #endif #ifdef USE_CRYPTO_GNUTLS - registerImpl_internal("gnutls"); + registerImpl_internal("gnutls", std::make_shared); #endif #ifdef USE_CRYPTO_OPENSSL - registerImpl_internal("openssl"); + registerImpl_internal("openssl", std::make_shared); #endif std::string default_crypto; if (!QUtil::get_env("QPDF_CRYPTO_PROVIDER", &default_crypto)) { @@ -80,11 +79,10 @@ QPDFCryptoProvider::getImpl_internal(std::string const& name) const return m->providers[name](); } -template void -QPDFCryptoProvider::registerImpl_internal(std::string const& name) +QPDFCryptoProvider::registerImpl_internal(std::string const& name, provider_fn f) { - m->providers[name] = std::make_shared; + m->providers[name] = std::move(f); } void diff --git a/libtests/CMakeLists.txt b/libtests/CMakeLists.txt index 7d2ecbd..db5dfa3 100644 --- a/libtests/CMakeLists.txt +++ b/libtests/CMakeLists.txt @@ -8,6 +8,7 @@ set(TEST_PROGRAMS buffer closed_file_input_source concatenate + crypto_provider dct_compress dct_uncompress flate diff --git a/libtests/crypto_provider.cc b/libtests/crypto_provider.cc new file mode 100644 index 0000000..f28f163 --- /dev/null +++ b/libtests/crypto_provider.cc @@ -0,0 +1,98 @@ +#include + +#include +#include +#include +#include +#include + +// This is a dummy crypto implementation that only implements MD5 digest. It doesn't work, but +// it enables us to exercise the wiring of registering, querying, and setting a crypto provider. +class Potato: public QPDFCryptoImpl +{ + public: + void + provideRandomData(unsigned char* data, size_t len) override + { + } + void + MD5_init() override + { + } + void + MD5_update(const unsigned char* data, size_t len) override + { + } + void + MD5_finalize() override + { + } + void + MD5_digest(unsigned char* digest) override + { + std::memcpy(digest, "0123456789abcdef", sizeof(QPDFCryptoImpl::MD5_Digest)); + } + void + SHA2_init(int bits) override + { + } + void + SHA2_update(const unsigned char* data, size_t len) override + { + } + void + SHA2_finalize() override + { + } + std::string + SHA2_digest() override + { + return std::string(); + } + void + RC4_init(const unsigned char* key_data, int key_len) override + { + } + void + RC4_process(const unsigned char* in_data, size_t len, unsigned char* out_data) override + { + } + void + RC4_finalize() override + { + } + void + rijndael_init( + bool encrypt, + const unsigned char* 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 + { + } +}; + +int +main() +{ + auto initial = QPDFCryptoProvider::getDefaultProvider(); + QPDFCryptoProvider::registerImpl("potato"); + QPDFCryptoProvider::setDefaultProvider("potato"); + assert(QPDFCryptoProvider::getDefaultProvider() == "potato"); + MD5 m; + m.encodeString("quack"); // anything + MD5::Digest d; + m.digest(d); + // hex for 0123456789abcdef + assert(m.unparse() == "30313233343536373839616263646566"); + std::cout << "assertions passed\n"; + return 0; +} diff --git a/libtests/qtest/crypto_provider.test b/libtests/qtest/crypto_provider.test new file mode 100644 index 0000000..73c6792 --- /dev/null +++ b/libtests/qtest/crypto_provider.test @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +require 5.008; +use warnings; +use strict; + +require TestDriver; + +my $td = new TestDriver('crypto_provider'); + +$td->runtest("QINTC", + {$td->COMMAND => "crypto_provider"}, + {$td->STRING => "assertions passed\n", + $td->EXIT_STATUS => 0}, + $td->NORMALIZE_NEWLINES); + +$td->report(1); -- libgit2 0.21.4