Skip to content

Commit bcbbfa6

Browse files
authored
Merge pull request #83 from cppalliance/HMAC
2 parents d14c1c6 + 17a616b commit bcbbfa6

File tree

8 files changed

+424
-2
lines changed

8 files changed

+424
-2
lines changed

include/boost/crypt/hash/detail/hasher_base_512.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ template <boost::crypt::size_t digest_size,
3535
typename Derived>
3636
class hasher_base_512
3737
{
38+
public:
39+
static constexpr boost::crypt::size_t block_size {64U};
40+
3841
protected:
3942

4043
// Use CRTP to make this constexpr with C++14
@@ -46,7 +49,7 @@ class hasher_base_512
4649
BOOST_CRYPT_GPU_ENABLED constexpr auto update(ForwardIter data, boost::crypt::size_t size) noexcept -> hasher_state;
4750

4851
boost::crypt::array<boost::crypt::uint32_t, intermediate_hash_size> intermediate_hash_ {};
49-
boost::crypt::array<boost::crypt::uint8_t , 64U> buffer_ {};
52+
boost::crypt::array<boost::crypt::uint8_t , block_size> buffer_ {};
5053
boost::crypt::size_t buffer_index_ {};
5154
boost::crypt::size_t low_ {};
5255
boost::crypt::size_t high_ {};

include/boost/crypt/hash/detail/sha3_base.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,17 @@ namespace hash_detail {
3636
template <boost::crypt::size_t digest_size, bool is_xof = false>
3737
class sha3_base
3838
{
39+
public:
40+
41+
static constexpr boost::crypt::size_t block_size {200U - 2U * digest_size};
42+
3943
private:
4044

4145
static_assert((!is_xof && (digest_size == 28U || digest_size == 32U || digest_size == 48U || digest_size == 64U)) || is_xof,
4246
"Digest size must be 28 (SHA3-224), 32 (SHA3-256), 48 (SHA3-384), or 64(SHA3-512) or this must be an xof");
4347

4448
boost::crypt::array<boost::crypt::uint64_t, 25U> state_array_ {};
45-
boost::crypt::array<boost::crypt::uint8_t, 200U - 2U * digest_size> buffer_ {};
49+
boost::crypt::array<boost::crypt::uint8_t, block_size> buffer_ {};
4650
boost::crypt::size_t buffer_index_ {};
4751
bool computed_ {};
4852
bool corrupted_ {};

include/boost/crypt/hash/detail/sha512_base.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ namespace hash_detail {
3535
template <boost::crypt::size_t digest_size>
3636
class sha512_base final
3737
{
38+
public:
39+
40+
static constexpr boost::crypt::size_t block_size {128U};
41+
3842
private:
3943

4044
static_assert(digest_size == 28U || digest_size == 32U || digest_size == 48U || digest_size == 64U,

include/boost/crypt/hash/hmac.hpp

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
// Copyright 2024 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#ifndef BOOST_CRYPT_HASH_HMAC_HPP
6+
#define BOOST_CRYPT_HASH_HMAC_HPP
7+
8+
#include <boost/crypt/hash/hasher_state.hpp>
9+
#include <boost/crypt/utility/config.hpp>
10+
#include <boost/crypt/utility/cstddef.hpp>
11+
#include <boost/crypt/utility/cstdint.hpp>
12+
#include <boost/crypt/utility/null.hpp>
13+
#include <boost/crypt/utility/array.hpp>
14+
15+
namespace boost {
16+
namespace crypt {
17+
18+
BOOST_CRYPT_EXPORT template <typename HasherType>
19+
class hmac
20+
{
21+
public:
22+
23+
static constexpr boost::crypt::size_t block_size_ {HasherType::block_size};
24+
using return_type = typename HasherType::return_type;
25+
using key_type = boost::crypt::array<boost::crypt::uint8_t, block_size_>;
26+
27+
private:
28+
29+
key_type inner_key_ {};
30+
key_type outer_key_ {};
31+
HasherType inner_hash_;
32+
HasherType outer_hash_;
33+
bool initialized_ {false};
34+
bool computed_ {false};
35+
bool corrupted_ {false};
36+
37+
public:
38+
39+
BOOST_CRYPT_GPU_ENABLED constexpr hmac() noexcept = default;
40+
41+
template <typename ForwardIter>
42+
BOOST_CRYPT_GPU_ENABLED constexpr hmac(ForwardIter key, boost::crypt::size_t size) noexcept { init(key, size); }
43+
44+
BOOST_CRYPT_GPU_ENABLED constexpr hmac(const key_type& inner_key, const key_type& outer_key) noexcept { init_from_keys(inner_key, outer_key); }
45+
46+
template <typename ForwardIter>
47+
BOOST_CRYPT_GPU_ENABLED constexpr auto init(ForwardIter key, boost::crypt::size_t size) noexcept -> hasher_state;
48+
49+
BOOST_CRYPT_GPU_ENABLED constexpr auto init_from_keys(const key_type& inner_key,
50+
const key_type& outer_key) noexcept -> hasher_state;
51+
52+
template <typename ForwardIter>
53+
BOOST_CRYPT_GPU_ENABLED constexpr auto process_bytes(ForwardIter data, boost::crypt::size_t size) noexcept -> hasher_state;
54+
55+
BOOST_CRYPT_GPU_ENABLED constexpr auto get_digest() noexcept -> return_type;
56+
57+
BOOST_CRYPT_GPU_ENABLED constexpr auto get_outer_key() noexcept -> key_type;
58+
59+
BOOST_CRYPT_GPU_ENABLED constexpr auto get_inner_key() noexcept -> key_type;
60+
};
61+
62+
template <typename HasherType>
63+
constexpr auto hmac<HasherType>::get_inner_key() noexcept -> key_type
64+
{
65+
return inner_key_;
66+
}
67+
68+
template <typename HasherType>
69+
constexpr auto hmac<HasherType>::get_outer_key() noexcept -> key_type
70+
{
71+
return outer_key_;
72+
}
73+
74+
template <typename HasherType>
75+
constexpr auto
76+
hmac<HasherType>::init_from_keys(const boost::crypt::array<boost::crypt::uint8_t, block_size_> &inner_key,
77+
const boost::crypt::array<boost::crypt::uint8_t, block_size_> &outer_key) noexcept -> hasher_state
78+
{
79+
computed_ = false;
80+
corrupted_ = false;
81+
inner_hash_.init();
82+
outer_hash_.init();
83+
84+
inner_key_ = inner_key;
85+
outer_key_ = outer_key;
86+
87+
const auto inner_result {inner_hash_.process_bytes(inner_key_.begin(), inner_key_.size())};
88+
const auto outer_result {outer_hash_.process_bytes(outer_key_.begin(), outer_key_.size())};
89+
90+
if (BOOST_CRYPT_LIKELY(inner_result == hasher_state::success && outer_result == hasher_state::success))
91+
{
92+
initialized_ = true;
93+
return hasher_state::success;
94+
}
95+
else
96+
{
97+
// If we have some weird OOM result
98+
// LCOV_EXCL_START
99+
if (inner_result != hasher_state::success)
100+
{
101+
return inner_result;
102+
}
103+
else
104+
{
105+
return outer_result;
106+
}
107+
// LCOV_EXCL_STOP
108+
}
109+
}
110+
111+
template <typename HasherType>
112+
constexpr auto hmac<HasherType>::get_digest() noexcept -> return_type
113+
{
114+
if (computed_)
115+
{
116+
corrupted_ = true;
117+
}
118+
if (corrupted_)
119+
{
120+
return return_type {};
121+
}
122+
123+
computed_ = true;
124+
const auto r_inner {inner_hash_.get_digest()};
125+
outer_hash_.process_bytes(r_inner.begin(), r_inner.size());
126+
return outer_hash_.get_digest();
127+
}
128+
129+
template <typename HasherType>
130+
template <typename ForwardIter>
131+
constexpr auto hmac<HasherType>::process_bytes(ForwardIter data, boost::crypt::size_t size) noexcept -> hasher_state
132+
{
133+
if (utility::is_null(data) || size == 0U)
134+
{
135+
return hasher_state::null;
136+
}
137+
else if (!initialized_ || corrupted_)
138+
{
139+
return hasher_state::state_error;
140+
}
141+
142+
const auto status_code {inner_hash_.process_bytes(data, size)};
143+
if (BOOST_CRYPT_LIKELY(status_code == hasher_state::success))
144+
{
145+
return hasher_state::success;
146+
}
147+
else
148+
{
149+
// Cannot test 64 and 128 bit OOM
150+
// LCOV_EXCL_START
151+
switch (status_code)
152+
{
153+
case hasher_state::state_error:
154+
corrupted_ = true;
155+
return hasher_state::state_error;
156+
case hasher_state::input_too_long:
157+
corrupted_ = true;
158+
return hasher_state::input_too_long;
159+
default:
160+
BOOST_CRYPT_UNREACHABLE;
161+
}
162+
// LCOV_EXCL_STOP
163+
}
164+
}
165+
166+
template <typename HasherType>
167+
template <typename ForwardIter>
168+
constexpr auto hmac<HasherType>::init(ForwardIter key, boost::crypt::size_t size) noexcept -> hasher_state
169+
{
170+
computed_ = false;
171+
corrupted_ = false;
172+
inner_hash_.init();
173+
outer_hash_.init();
174+
175+
boost::crypt::array<boost::crypt::uint8_t, block_size_> k0 {};
176+
177+
if (utility::is_null(key) || size == 0U)
178+
{
179+
return hasher_state::null;
180+
}
181+
182+
// Step 1: If the length of K = B set K0 = K. Go to step 4
183+
// OR
184+
// Step 3: If the length of K < B: append zeros to the end of K.
185+
if (size <= block_size_)
186+
{
187+
auto key_iter {key};
188+
for (boost::crypt::size_t i {}; i < size; ++i)
189+
{
190+
k0[i] = static_cast<boost::crypt::uint8_t>(*key_iter++);
191+
}
192+
}
193+
// Step 2: If the length of K > B: hash K to obtain an L byte string
194+
else if (size > block_size_)
195+
{
196+
HasherType hasher;
197+
hasher.process_bytes(key, size);
198+
const auto res {hasher.get_digest()};
199+
200+
BOOST_CRYPT_ASSERT(res.size() <= k0.size());
201+
202+
for (boost::crypt::size_t i {}; i < res.size(); ++i)
203+
{
204+
k0[i] = res[i];
205+
}
206+
}
207+
208+
// Step 4: XOR k0 with ipad to produce a B-byte string K0 ^ ipad
209+
// Step 7: XOR k0 with opad to produce a B-byte string K0 ^ opad
210+
for (boost::crypt::size_t i {}; i < k0.size(); ++i)
211+
{
212+
inner_key_[i] = static_cast<boost::crypt::uint8_t>(k0[i] ^ static_cast<boost::crypt::size_t>(0x36));
213+
outer_key_[i] = static_cast<boost::crypt::uint8_t>(k0[i] ^ static_cast<boost::crypt::size_t>(0x5c));
214+
}
215+
216+
const auto inner_result {inner_hash_.process_bytes(inner_key_.begin(), inner_key_.size())};
217+
const auto outer_result {outer_hash_.process_bytes(outer_key_.begin(), outer_key_.size())};
218+
219+
if (BOOST_CRYPT_LIKELY(inner_result == hasher_state::success && outer_result == hasher_state::success))
220+
{
221+
initialized_ = true;
222+
return hasher_state::success;
223+
}
224+
else
225+
{
226+
// If we have some weird OOM result
227+
// LCOV_EXCL_START
228+
if (inner_result != hasher_state::success)
229+
{
230+
return inner_result;
231+
}
232+
else
233+
{
234+
return outer_result;
235+
}
236+
// LCOV_EXCL_STOP
237+
}
238+
}
239+
240+
} // namespace crypt
241+
} // namespace boost
242+
243+
#endif //BOOST_CRYPT_HASH_HMAC_HPP

include/boost/crypt/hash/md5.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <boost/crypt/hash/detail/hasher_base_512.hpp>
1313
#include <boost/crypt/hash/hasher_state.hpp>
14+
#include <boost/crypt/hash/hmac.hpp>
1415
#include <boost/crypt/utility/config.hpp>
1516
#include <boost/crypt/utility/bit.hpp>
1617
#include <boost/crypt/utility/byte.hpp>

include/boost/crypt/hash/sha1.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <boost/crypt/hash/detail/hasher_base_512.hpp>
1111
#include <boost/crypt/hash/hasher_state.hpp>
12+
#include <boost/crypt/hash/hmac.hpp>
1213
#include <boost/crypt/utility/config.hpp>
1314
#include <boost/crypt/utility/bit.hpp>
1415
#include <boost/crypt/utility/byte.hpp>

test/Jamfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ run test_sha3_224.cpp ;
6464
run test_shake128.cpp ;
6565
run test_shake256.cpp ;
6666

67+
run test_hmac.cpp ;
68+
6769
# NIST standard testing
6870
run test_nist_cavs_sha1_monte.cpp ;
6971
run test_nist_cavs_sha1_short_long.cpp ;

0 commit comments

Comments
 (0)