diff --git a/src/zm_crypt.h b/src/zm_crypt.h index 15dbc9332..8803c0e54 100644 --- a/src/zm_crypt.h +++ b/src/zm_crypt.h @@ -20,10 +20,35 @@ #ifndef ZM_CRYPT_H #define ZM_CRYPT_H +#include "zm_config.h" +#include "zm_crypto_gnutls.h" +#include "zm_crypto_openssl.h" +#include "zm_define.h" #include -#include -bool verifyPassword( const char *username, const char *input_password, const char *db_password_hash); +bool verifyPassword(const char *username, const char *input_password, const char *db_password_hash); -std::pair verifyToken(std::string token, std::string key); -#endif // ZM_CRYPT_H \ No newline at end of file +std::pair verifyToken(std::string token, std::string key); + +namespace zm { +namespace crypto { +namespace impl { + +#if defined(HAVE_LIBGNUTLS) +template +using Hash = gnutls::GenericHashImpl; +#elif defined(HAVE_LIBOPENSSL) +template +using Hash = openssl::GenericHashImpl; +#endif +} +} +} + +namespace zm { +namespace crypto { +using MD5 = impl::Hash; +} +} + +#endif // ZM_CRYPT_H diff --git a/src/zm_crypto_generics.h b/src/zm_crypto_generics.h new file mode 100644 index 000000000..4ceb92022 --- /dev/null +++ b/src/zm_crypto_generics.h @@ -0,0 +1,102 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ZONEMINDER_SRC_ZM_CRYPTO_GENERICS_H_ +#define ZONEMINDER_SRC_ZM_CRYPTO_GENERICS_H_ + +#include "zm_define.h" +#include "zm_utils.h" +#include +#include + +namespace zm { +namespace crypto { +namespace impl { + +enum class HashAlgorithms { + kMD5 +}; + +template +struct HashAlgorithm; + +template<> +struct HashAlgorithm { + static constexpr size_t digest_length = 16; +}; + +template +class GenericHash { + public: + static constexpr size_t DIGEST_LENGTH = HashAlgorithm::digest_length; + using Digest = std::array; + + static Digest GetDigestOf(uint8 const *data, size_t len) { + Impl hash; + hash.UpdateData(data, len); + hash.Finalize(); + return hash.GetDigest(); + } + + template + static Digest GetDigestOf(Ts &&... pack) { + Impl hash; + UpdateData(hash, std::forward(pack)...); + hash.Finalize(); + return hash.GetDigest(); + } + + void UpdateData(const uint8 *data, size_t length) { + static_cast(*this).DoUpdateData(data, length); + } + void UpdateData(const std::string &str) { + UpdateData(reinterpret_cast(str.c_str()), str.size()); + } + void UpdateData(const char *str) { + UpdateData(reinterpret_cast(str), strlen(str)); + } + template + void UpdateData(Container const &c) { + UpdateData(ZM::data(c), ZM::size(c)); + } + + void Finalize() { + static_cast(*this).DoFinalize(); + } + + const Digest &GetDigest() const { return digest_; } + + protected: + Digest digest_ = {}; + + private: + template + static void UpdateData(Impl &hash, T const &data) { + hash.UpdateData(data); + } + + template + static void UpdateData(Impl &hash, T const &data, TRest &&... rest) { + hash.UpdateData(data); + UpdateData(hash, std::forward(rest)...); + } +}; +} +} +} + +#endif //ZONEMINDER_SRC_ZM_CRYPTO_GENERICS_H_ diff --git a/src/zm_crypto_gnutls.h b/src/zm_crypto_gnutls.h new file mode 100644 index 000000000..e727144a0 --- /dev/null +++ b/src/zm_crypto_gnutls.h @@ -0,0 +1,70 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ZONEMINDER_SRC_ZM_CRYPTO_GNUTLS_H_ +#define ZONEMINDER_SRC_ZM_CRYPTO_GNUTLS_H_ + +#ifdef HAVE_LIBGNUTLS + +#include "zm_crypto_generics.h" +#include "zm_utils.h" +#include + +namespace zm { +namespace crypto { +namespace impl { +namespace gnutls { + +template +struct HashAlgorithmMapper; + +template<> +struct HashAlgorithmMapper { + static constexpr gnutls_digest_algorithm_t algorithm = GNUTLS_DIG_MD5; +}; + +template +class GenericHashImpl : public GenericHash, Algorithm> { + public: + GenericHashImpl() { + int32 ret = gnutls_hash_init(&handle_, HashAlgorithmMapper::algorithm); + ASSERT(ret == 0); + }; + + void DoUpdateData(const uint8 *data, size_t length) { + int32 res = gnutls_hash(handle_, data, length); + ASSERT(res == 0); + } + + void DoFinalize() { + gnutls_hash_deinit(handle_, digest_.data()); + } + + private: + gnutls_hash_hd_t handle_ = {}; + + using Base = GenericHash, Algorithm>; + using Base::digest_; +}; +} +} +} +} + +#endif // HAVE_LIBGNUTLS + +#endif // ZONEMINDER_SRC_ZM_CRYPTO_GNUTLS_H_ diff --git a/src/zm_crypto_openssl.h b/src/zm_crypto_openssl.h new file mode 100644 index 000000000..87f96eedd --- /dev/null +++ b/src/zm_crypto_openssl.h @@ -0,0 +1,94 @@ +/* + * This file is part of the ZoneMinder Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ZONEMINDER_SRC_ZM_CRYPTO_OPENSSL_H_ +#define ZONEMINDER_SRC_ZM_CRYPTO_OPENSSL_H_ + +#ifdef HAVE_LIBOPENSSL + +#include "zm_crypto_generics.h" +#include "zm_utils.h" +#include + +namespace zm { +namespace crypto { +namespace impl { +namespace openssl { + +typedef EVP_MD const *(*HashCreator)(); + +template +struct HashAlgorithmMapper; + +template<> +struct HashAlgorithmMapper { +// TODO: Remove conditional once Jessie and CentOS 7 are deprecated +// This is needed since GCC 4.8 is faulty (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60199) +#if defined(__GNUC__) && __GNUC__ < 5 + static HashCreator hash_creator() { + static constexpr HashCreator creator = EVP_md5; + return creator; + } +#else + static constexpr HashCreator hash_creator = EVP_md5; +#endif +}; + +template +class GenericHashImpl : public GenericHash, Algorithm> { + public: + GenericHashImpl() { + // TODO: Use EVP_MD_CTX_new once we drop support for Jessie and CentOS 7 (OpenSSL > 1.1.0) + ctx_ = EVP_MD_CTX_create(); +#if defined(__GNUC__) && __GNUC__ < 5 + EVP_DigestInit_ex(ctx_, HashAlgorithmMapper::hash_creator()(), nullptr); +#else + EVP_DigestInit_ex(ctx_, HashAlgorithmMapper::hash_creator(), nullptr); +#endif + }; + + ~GenericHashImpl() { + // TODO: Use EVP_MD_CTX_free once we drop support for Jessie and CentOS 7 (OpenSSL > 1.1.0) + EVP_MD_CTX_destroy(ctx_); + } + + void DoUpdateData(const uint8 *data, size_t length) { + int32 res = EVP_DigestUpdate(ctx_, data, length); + ASSERT(res == 1); + } + + void DoFinalize() { + uint32 length = 0; + int32 res = EVP_DigestFinal_ex(ctx_, digest_.data(), &length); + ASSERT(res == 1); + ASSERT(length == HashAlgorithm::digest_length); + } + + private: + EVP_MD_CTX *ctx_; + + using Base = GenericHash, Algorithm>; + using Base::digest_; +}; +} +} +} +} + +#endif // HAVE_LIBOPENSSL + +#endif // ZONEMINDER_SRC_ZM_CRYPTO_OPENSSL_H_ diff --git a/tests/zm_crypt.cpp b/tests/zm_crypt.cpp index 0d68c1e3c..ba432d695 100644 --- a/tests/zm_crypt.cpp +++ b/tests/zm_crypt.cpp @@ -76,3 +76,52 @@ TEST_CASE("JWT validation") { REQUIRE(result.second == 0); } } + +TEST_CASE("zm::crypto::MD5") { + using namespace zm::crypto; + MD5 md5; + + REQUIRE(md5.GetDigest() == MD5::Digest()); + + SECTION("hash from const char*") { + md5.UpdateData("abcdefghijklmnopqrstuvwxyz"); + md5.Finalize(); + + REQUIRE(md5.GetDigest() == MD5::Digest{0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, + 0x67, 0xe1, 0x3b}); + } + + SECTION("hash from std::string") { + md5.UpdateData(std::string("abcdefghijklmnopqrstuvwxyz")); + md5.Finalize(); + + REQUIRE(md5.GetDigest() == MD5::Digest{0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, + 0x67, 0xe1, 0x3b}); + } +} + +TEST_CASE("zm::crypto::MD5::GetDigestOf") { + using namespace zm::crypto; + std::array data = {'a', 'b', 'c'}; + + SECTION("data and len") { + MD5::Digest digest = MD5::GetDigestOf(reinterpret_cast(data.data()), data.size()); + + REQUIRE(digest == MD5::Digest{0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, + 0x7f, 0x72}); + } + + SECTION("container") { + MD5::Digest digest = MD5::GetDigestOf(data); + + REQUIRE(digest == MD5::Digest{0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, + 0x7f, 0x72}); + } + + SECTION("multiple containers") { + MD5::Digest digest = MD5::GetDigestOf(data, data); + + REQUIRE(digest == MD5::Digest{0x44, 0x0a, 0xc8, 0x58, 0x92, 0xca, 0x43, 0xad, 0x26, 0xd4, 0x4c, 0x7a, 0xd9, 0xd4, + 0x7d, 0x3e}); + } +}