Skip to content

Commit

Permalink
Merge pull request #83 from cppalliance/HMAC
Browse files Browse the repository at this point in the history
  • Loading branch information
mborland authored Nov 5, 2024
2 parents d14c1c6 + 17a616b commit bcbbfa6
Show file tree
Hide file tree
Showing 8 changed files with 424 additions and 2 deletions.
5 changes: 4 additions & 1 deletion include/boost/crypt/hash/detail/hasher_base_512.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ template <boost::crypt::size_t digest_size,
typename Derived>
class hasher_base_512
{
public:
static constexpr boost::crypt::size_t block_size {64U};

protected:

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

boost::crypt::array<boost::crypt::uint32_t, intermediate_hash_size> intermediate_hash_ {};
boost::crypt::array<boost::crypt::uint8_t , 64U> buffer_ {};
boost::crypt::array<boost::crypt::uint8_t , block_size> buffer_ {};
boost::crypt::size_t buffer_index_ {};
boost::crypt::size_t low_ {};
boost::crypt::size_t high_ {};
Expand Down
6 changes: 5 additions & 1 deletion include/boost/crypt/hash/detail/sha3_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,17 @@ namespace hash_detail {
template <boost::crypt::size_t digest_size, bool is_xof = false>
class sha3_base
{
public:

static constexpr boost::crypt::size_t block_size {200U - 2U * digest_size};

private:

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

boost::crypt::array<boost::crypt::uint64_t, 25U> state_array_ {};
boost::crypt::array<boost::crypt::uint8_t, 200U - 2U * digest_size> buffer_ {};
boost::crypt::array<boost::crypt::uint8_t, block_size> buffer_ {};
boost::crypt::size_t buffer_index_ {};
bool computed_ {};
bool corrupted_ {};
Expand Down
4 changes: 4 additions & 0 deletions include/boost/crypt/hash/detail/sha512_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ namespace hash_detail {
template <boost::crypt::size_t digest_size>
class sha512_base final
{
public:

static constexpr boost::crypt::size_t block_size {128U};

private:

static_assert(digest_size == 28U || digest_size == 32U || digest_size == 48U || digest_size == 64U,
Expand Down
243 changes: 243 additions & 0 deletions include/boost/crypt/hash/hmac.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
// Copyright 2024 Matt Borland
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#ifndef BOOST_CRYPT_HASH_HMAC_HPP
#define BOOST_CRYPT_HASH_HMAC_HPP

#include <boost/crypt/hash/hasher_state.hpp>
#include <boost/crypt/utility/config.hpp>
#include <boost/crypt/utility/cstddef.hpp>
#include <boost/crypt/utility/cstdint.hpp>
#include <boost/crypt/utility/null.hpp>
#include <boost/crypt/utility/array.hpp>

namespace boost {
namespace crypt {

BOOST_CRYPT_EXPORT template <typename HasherType>
class hmac
{
public:

static constexpr boost::crypt::size_t block_size_ {HasherType::block_size};
using return_type = typename HasherType::return_type;
using key_type = boost::crypt::array<boost::crypt::uint8_t, block_size_>;

private:

key_type inner_key_ {};
key_type outer_key_ {};
HasherType inner_hash_;
HasherType outer_hash_;
bool initialized_ {false};
bool computed_ {false};
bool corrupted_ {false};

public:

BOOST_CRYPT_GPU_ENABLED constexpr hmac() noexcept = default;

template <typename ForwardIter>
BOOST_CRYPT_GPU_ENABLED constexpr hmac(ForwardIter key, boost::crypt::size_t size) noexcept { init(key, size); }

BOOST_CRYPT_GPU_ENABLED constexpr hmac(const key_type& inner_key, const key_type& outer_key) noexcept { init_from_keys(inner_key, outer_key); }

template <typename ForwardIter>
BOOST_CRYPT_GPU_ENABLED constexpr auto init(ForwardIter key, boost::crypt::size_t size) noexcept -> hasher_state;

BOOST_CRYPT_GPU_ENABLED constexpr auto init_from_keys(const key_type& inner_key,
const key_type& outer_key) noexcept -> hasher_state;

template <typename ForwardIter>
BOOST_CRYPT_GPU_ENABLED constexpr auto process_bytes(ForwardIter data, boost::crypt::size_t size) noexcept -> hasher_state;

BOOST_CRYPT_GPU_ENABLED constexpr auto get_digest() noexcept -> return_type;

BOOST_CRYPT_GPU_ENABLED constexpr auto get_outer_key() noexcept -> key_type;

BOOST_CRYPT_GPU_ENABLED constexpr auto get_inner_key() noexcept -> key_type;
};

template <typename HasherType>
constexpr auto hmac<HasherType>::get_inner_key() noexcept -> key_type
{
return inner_key_;
}

template <typename HasherType>
constexpr auto hmac<HasherType>::get_outer_key() noexcept -> key_type
{
return outer_key_;
}

template <typename HasherType>
constexpr auto
hmac<HasherType>::init_from_keys(const boost::crypt::array<boost::crypt::uint8_t, block_size_> &inner_key,
const boost::crypt::array<boost::crypt::uint8_t, block_size_> &outer_key) noexcept -> hasher_state
{
computed_ = false;
corrupted_ = false;
inner_hash_.init();
outer_hash_.init();

inner_key_ = inner_key;
outer_key_ = outer_key;

const auto inner_result {inner_hash_.process_bytes(inner_key_.begin(), inner_key_.size())};
const auto outer_result {outer_hash_.process_bytes(outer_key_.begin(), outer_key_.size())};

if (BOOST_CRYPT_LIKELY(inner_result == hasher_state::success && outer_result == hasher_state::success))
{
initialized_ = true;
return hasher_state::success;
}
else
{
// If we have some weird OOM result
// LCOV_EXCL_START
if (inner_result != hasher_state::success)
{
return inner_result;
}
else
{
return outer_result;
}
// LCOV_EXCL_STOP
}
}

template <typename HasherType>
constexpr auto hmac<HasherType>::get_digest() noexcept -> return_type
{
if (computed_)
{
corrupted_ = true;
}
if (corrupted_)
{
return return_type {};
}

computed_ = true;
const auto r_inner {inner_hash_.get_digest()};
outer_hash_.process_bytes(r_inner.begin(), r_inner.size());
return outer_hash_.get_digest();
}

template <typename HasherType>
template <typename ForwardIter>
constexpr auto hmac<HasherType>::process_bytes(ForwardIter data, boost::crypt::size_t size) noexcept -> hasher_state
{
if (utility::is_null(data) || size == 0U)
{
return hasher_state::null;
}
else if (!initialized_ || corrupted_)
{
return hasher_state::state_error;
}

const auto status_code {inner_hash_.process_bytes(data, size)};
if (BOOST_CRYPT_LIKELY(status_code == hasher_state::success))
{
return hasher_state::success;
}
else
{
// Cannot test 64 and 128 bit OOM
// LCOV_EXCL_START
switch (status_code)
{
case hasher_state::state_error:
corrupted_ = true;
return hasher_state::state_error;
case hasher_state::input_too_long:
corrupted_ = true;
return hasher_state::input_too_long;
default:
BOOST_CRYPT_UNREACHABLE;
}
// LCOV_EXCL_STOP
}
}

template <typename HasherType>
template <typename ForwardIter>
constexpr auto hmac<HasherType>::init(ForwardIter key, boost::crypt::size_t size) noexcept -> hasher_state
{
computed_ = false;
corrupted_ = false;
inner_hash_.init();
outer_hash_.init();

boost::crypt::array<boost::crypt::uint8_t, block_size_> k0 {};

if (utility::is_null(key) || size == 0U)
{
return hasher_state::null;
}

// Step 1: If the length of K = B set K0 = K. Go to step 4
// OR
// Step 3: If the length of K < B: append zeros to the end of K.
if (size <= block_size_)
{
auto key_iter {key};
for (boost::crypt::size_t i {}; i < size; ++i)
{
k0[i] = static_cast<boost::crypt::uint8_t>(*key_iter++);
}
}
// Step 2: If the length of K > B: hash K to obtain an L byte string
else if (size > block_size_)
{
HasherType hasher;
hasher.process_bytes(key, size);
const auto res {hasher.get_digest()};

BOOST_CRYPT_ASSERT(res.size() <= k0.size());

for (boost::crypt::size_t i {}; i < res.size(); ++i)
{
k0[i] = res[i];
}
}

// Step 4: XOR k0 with ipad to produce a B-byte string K0 ^ ipad
// Step 7: XOR k0 with opad to produce a B-byte string K0 ^ opad
for (boost::crypt::size_t i {}; i < k0.size(); ++i)
{
inner_key_[i] = static_cast<boost::crypt::uint8_t>(k0[i] ^ static_cast<boost::crypt::size_t>(0x36));
outer_key_[i] = static_cast<boost::crypt::uint8_t>(k0[i] ^ static_cast<boost::crypt::size_t>(0x5c));
}

const auto inner_result {inner_hash_.process_bytes(inner_key_.begin(), inner_key_.size())};
const auto outer_result {outer_hash_.process_bytes(outer_key_.begin(), outer_key_.size())};

if (BOOST_CRYPT_LIKELY(inner_result == hasher_state::success && outer_result == hasher_state::success))
{
initialized_ = true;
return hasher_state::success;
}
else
{
// If we have some weird OOM result
// LCOV_EXCL_START
if (inner_result != hasher_state::success)
{
return inner_result;
}
else
{
return outer_result;
}
// LCOV_EXCL_STOP
}
}

} // namespace crypt
} // namespace boost

#endif //BOOST_CRYPT_HASH_HMAC_HPP
1 change: 1 addition & 0 deletions include/boost/crypt/hash/md5.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <boost/crypt/hash/detail/hasher_base_512.hpp>
#include <boost/crypt/hash/hasher_state.hpp>
#include <boost/crypt/hash/hmac.hpp>
#include <boost/crypt/utility/config.hpp>
#include <boost/crypt/utility/bit.hpp>
#include <boost/crypt/utility/byte.hpp>
Expand Down
1 change: 1 addition & 0 deletions include/boost/crypt/hash/sha1.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <boost/crypt/hash/detail/hasher_base_512.hpp>
#include <boost/crypt/hash/hasher_state.hpp>
#include <boost/crypt/hash/hmac.hpp>
#include <boost/crypt/utility/config.hpp>
#include <boost/crypt/utility/bit.hpp>
#include <boost/crypt/utility/byte.hpp>
Expand Down
2 changes: 2 additions & 0 deletions test/Jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ run test_sha3_224.cpp ;
run test_shake128.cpp ;
run test_shake256.cpp ;

run test_hmac.cpp ;

# NIST standard testing
run test_nist_cavs_sha1_monte.cpp ;
run test_nist_cavs_sha1_short_long.cpp ;
Expand Down
Loading

0 comments on commit bcbbfa6

Please sign in to comment.