CsCrypto  1.0.1
hash_append.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_HASH_APPEND_H
21 #define CS_CRYPTO_HASH_APPEND_H
22 
23 #include <util/conversions/byte.h>
24 #include <util/tools/crypto_traits.h>
25 #include <util/tools/is_detected_traits.h>
26 #include <util/tools/span.h>
27 
28 #include <array>
29 #include <cstddef>
30 #include <cstring>
31 #include <type_traits>
32 #include <vector>
33 
34 namespace cs_crypto::hash {
35 
36 template <typename T>
37 using update_member_fn = decltype(std::declval<T &>().update(std::declval<cs_crypto::util::span<std::byte>>()));
38 
39 template <typename T>
40 using update_free_fn = decltype(update(std::declval<T &>(), std::declval<cs_crypto::util::span<std::byte>>()));
41 
42 struct update_dispatch_fn {
43  template <typename HashContext>
44  constexpr auto operator()(HashContext &ctx, cs_crypto::util::span<std::byte> bytes) const
45  {
46  if constexpr (traits::is_detected_v<update_member_fn, HashContext>) {
47  return ctx.update(bytes);
48 
49  } else if constexpr (traits::is_detected_v<update_free_fn, HashContext>) {
50  return update(ctx, bytes);
51 
52  } else {
53  static_assert(traits::always_false<HashContext>{},
54  "Driver incomplete, unable to locate update() as a method or free function");
55  }
56  }
57 };
58 
59 inline constexpr const update_dispatch_fn dispatch_update{};
60 
61 template <typename HashContext, typename T,
62  typename = std::enable_if_t<cs_crypto::traits::is_uniquely_represented_byte_v<T>>>
63 constexpr auto internal_hash_append(HashContext &ctx, const T v)
64 {
65  return dispatch_update(ctx, {util::to_byte_ptr(std::addressof(v)), 1});
66 }
67 
68 template <typename HashContext, typename T, std::size_t N,
69  typename = std::enable_if_t<cs_crypto::traits::is_uniquely_represented_byte_v<T>>>
70 constexpr auto internal_hash_append(HashContext &ctx, const T (&arr)[N])
71 {
72  return dispatch_update(ctx, {util::to_byte_ptr(std::addressof(arr[0])), N});
73 }
74 
75 template <typename HashContext, typename T, std::size_t N,
76  typename = std::enable_if_t<cs_crypto::traits::is_uniquely_represented_byte_v<T>>>
77 constexpr auto internal_hash_append(HashContext &ctx, const std::array<T, N> &data)
78 {
79  return dispatch_update(ctx, {util::to_byte_ptr(data.data()), data.size()});
80 }
81 
82 template <typename HashContext, typename T,
83  typename = std::enable_if_t<cs_crypto::traits::is_uniquely_represented_byte_v<T>>>
84 constexpr auto internal_hash_append(HashContext &ctx, const std::vector<T> &data)
85 {
86  return dispatch_update(ctx, {util::to_byte_ptr(data.data()), data.size()});
87 }
88 
89 template <typename HashContext>
90 constexpr auto internal_hash_append(HashContext &ctx, const std::string &s)
91 {
92  return dispatch_update(ctx, {util::to_byte_ptr(s.data()), s.size()});
93 }
94 
95 template <typename HashContext, typename T, typename = std::enable_if_t<traits::is_iterable_v<T>>>
96 constexpr auto internal_hash_append(HashContext &ctx, const T &data)
97 {
98  // error handling left unimplemented until user experience is reported
99 
100  for (const auto elem : data) {
101  internal_hash_append(ctx, elem);
102  }
103 }
104 
105 template <typename HashContext, typename It, typename End>
106 constexpr auto internal_hash_append(HashContext &ctx, It iter, End end)
107 {
108  // error handling left unimplemented until user experience is reported
109 
110  while (iter != end) {
111  internal_hash_append(ctx, *iter);
112  iter = std::next(iter);
113  }
114 }
115 
116 template <typename Ctx>
117 [[maybe_unused]] inline constexpr bool is_appendable_hash_context_v =
118  std::is_copy_assignable_v<Ctx> && std::is_copy_constructible_v<Ctx> &&
119  std::is_move_assignable_v<Ctx> && std::is_move_constructible_v<Ctx> &&
120  (traits::is_detected_v<update_free_fn, Ctx> || traits::is_detected_v<update_member_fn, Ctx>);
121 
122 struct hash_append_internal {
123  template <typename HashContext, typename... Ts>
124  constexpr auto operator()(HashContext && context, Ts &&... args) const
125  {
126  static_assert(! std::is_same_v<HashContext, cs_crypto::traits::nonesuch>,
127  "Selected driver does not support this operation");
128 
129  static_assert(is_appendable_hash_context_v<std::remove_reference_t<HashContext>>,
130  "Hash context does not satisfy is_appendable_hash_context type trait");
131 
132  return internal_hash_append(std::forward<HashContext>(context), std::forward<Ts>(args)...);
133  }
134 };
135 
136 inline constexpr const hash_append_internal hash_append = {};
137 
138 } // namespace cs_crypto::hash
139 
140 #endif