CsCrypto  1.0.1
hash.h
1 /***********************************************************************
2 *
3 * Copyright (c) 2021-2024 Tim van Deurzen
4 * Copyright (c) 2021-2024 Barbara Geller
5 * Copyright (c) 2021-2024 Ansel Sermersheim
6 *
7 * This file is part of CsCrypto.
8 *
9 * CsCrypto is free software, released under the BSD 2-Clause license.
10 * For license details refer to LICENSE provided with this project.
11 *
12 * CsCrypto is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 *
16 * https://opensource.org/licenses/BSD-2-Clause
17 *
18 ***********************************************************************/
19 
20 #ifndef CS_CRYPTO_DRIVERS_OPENSSL_HASH_H
21 #define CS_CRYPTO_DRIVERS_OPENSSL_HASH_H
22 
23 #ifdef CSCRYPTO_HAVE_OPENSSL
24 
25 #include <drivers/base/hash.h>
26 #include <util/conversions/byte.h>
27 #include <util/tools/crypto_traits.h>
28 #include <util/tools/span.h>
29 
30 #include <array>
31 #include <memory>
32 #include <cstddef>
33 #include <optional>
34 #include <iostream>
35 
36 #include <openssl/evp.h>
37 #include <openssl/md4.h>
38 #include <openssl/md5.h>
39 #include <openssl/sha.h>
40 
41 namespace cs_crypto::drivers::openssl {
42 
43 template <typename OpenSSLContext, std::size_t SIZE, auto doHash_Init, auto doHash_Update, auto doHash_Finalize>
44 struct hasher_interface {
45  public:
46  constexpr static const std::size_t digest_size = SIZE;
47 
48  ~hasher_interface() = default;
49 
50  hasher_interface(const hasher_interface &other) = default;
51  hasher_interface &operator=(const hasher_interface &other) = default;
52 
53  hasher_interface(hasher_interface &&other) = default;
54  hasher_interface &operator=(hasher_interface &&other) = default;
55 
56  static std::optional<hasher_interface> make_context()
57  {
58  OpenSSLContext ctx;
59 
60  // doHash_Init refers to an init function in OpenSSL (SHA1_Init, MD5_Init, etc)
61  if (doHash_Init(&ctx) != 1) {
62  return std::nullopt;
63  }
64 
65  return hasher_interface(std::move(ctx));
66  }
67 
68  void update(cs_crypto::util::span<std::byte> bytes) &
69  {
70  // doHash_Update refers to an update function in OpenSSL (SHA1_Update, MD5_Update, etc)
71  doHash_Update(&m_context, bytes.data(), bytes.size());
72  }
73 
74  auto finalize() &&
75  {
76  std::array<std::byte, digest_size> md{};
77 
78  // doHash_Finalize refers to a finalize function in OpenSSL (SHA1_Final, MD5_Final, etc)
79  doHash_Finalize(util::from_byte_ptr(md.data()), &m_context);
80 
81  return md;
82  }
83 
84  private:
85  OpenSSLContext m_context;
86 
87  explicit hasher_interface(OpenSSLContext &&context)
88  : m_context(std::move(context))
89  {
90  }
91 };
92 
93 template <auto DigestInitFn, std::size_t SIZE>
94 struct keccak_interface {
95  public:
96  constexpr static const std::size_t digest_size = SIZE;
97 
98  keccak_interface(keccak_interface const &other)
99  : keccak_interface()
100  {
101  if (this == &other) {
102  return;
103  }
104 
105  EVP_MD_CTX_copy_ex(this->m_context.get(), other.m_context.get());
106  }
107 
108  ~keccak_interface() = default;
109 
110  keccak_interface &operator=(keccak_interface const & other)
111  {
112  auto tmp = other;
113  std::swap(this->m_context, tmp.m_context);
114 
115  return *this;
116  }
117 
118  keccak_interface(keccak_interface &&) = default;
119  keccak_interface &operator=(keccak_interface &&) = default;
120 
121  static std::optional<keccak_interface> make_context()
122  {
123  keccak_interface retval = {};
124 
125  if (retval.m_context == nullptr) {
126  return std::nullopt;
127  }
128 
129  return retval;
130  }
131 
132  void update(cs_crypto::util::span<std::byte> bytes) &
133  {
134  EVP_DigestUpdate(m_context.get(), bytes.data(), bytes.size());
135  }
136 
137  auto finalize() &&
138  {
139  std::array<std::byte, digest_size> result = {};
140  unsigned int sz = 0;
141 
142  EVP_DigestFinal_ex(m_context.get(), util::from_byte_ptr(result.data()), &sz);
143 
144  return result;
145  }
146 
147  private:
148  std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)> m_context;
149 
150  keccak_interface()
151  : m_context{EVP_MD_CTX_new(), ::EVP_MD_CTX_free}
152  {
153  if (m_context != nullptr && EVP_DigestInit_ex(m_context.get(), DigestInitFn(), nullptr) != 1) {
154  EVP_MD_CTX_free(m_context.get());
155  m_context = nullptr;
156  }
157  }
158 };
159 
160 struct hash : cs_crypto::drivers::basic_hash {
161  using md4 = hasher_interface<MD4_CTX, MD4_DIGEST_LENGTH, MD4_Init, MD4_Update, MD4_Final>;
162  using md5 = hasher_interface<MD5_CTX, MD5_DIGEST_LENGTH, MD5_Init, MD5_Update, MD5_Final>;
163 
164  using sha1 = hasher_interface<SHA_CTX, SHA_DIGEST_LENGTH, SHA1_Init, SHA1_Update, SHA1_Final>;
165 
166  using sha2_224 = hasher_interface<SHA256_CTX, SHA224_DIGEST_LENGTH, SHA224_Init, SHA224_Update, SHA224_Final>;
167  using sha2_256 = hasher_interface<SHA256_CTX, SHA256_DIGEST_LENGTH, SHA256_Init, SHA256_Update, SHA256_Final>;
168  using sha2_384 = hasher_interface<SHA512_CTX, SHA384_DIGEST_LENGTH, SHA384_Init, SHA384_Update, SHA384_Final>;
169  using sha2_512 = hasher_interface<SHA512_CTX, SHA512_DIGEST_LENGTH, SHA512_Init, SHA512_Update, SHA512_Final>;
170 
171  using sha3_224 = keccak_interface<EVP_sha3_224, 28>;
172  using sha3_256 = keccak_interface<EVP_sha3_256, 32>;
173  using sha3_384 = keccak_interface<EVP_sha3_384, 48>;
174  using sha3_512 = keccak_interface<EVP_sha3_512, 64>;
175 };
176 
177 } // namespace cs_crypto::drivers::openssl
178 
179 #endif // CSCRYPTO_HAVE_OPENSSL
180 
181 #endif