1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-25 14:35:07 +00:00
serenity/Userland/Libraries/LibCrypto/Authentication/HMAC.h
Daniel Bertalan a2aae6a582 LibCrypto: Remove redundant __builtin_memset() call
This call caused GCC 12's static analyzer to think that we perform an
out-of-bounds write to the v_key Vector. This is obviously incorrect,
and comes from the fact that GCC doesn't properly track whether we use
the inline storage, or the Vector is allocated on the heap.

While searching for a workaround, Sam pointed out that this call is
redundant as `Vector::resize()` already zeroes out the elements, so we
can completely remove it.

Co-authored-by: Sam Atkins <atkinssj@serenityos.org>
2021-12-24 14:35:33 -08:00

119 lines
3.5 KiB
C++

/*
* Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/ByteBuffer.h>
#include <AK/String.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <AK/Types.h>
#include <AK/Vector.h>
constexpr static auto IPAD = 0x36;
constexpr static auto OPAD = 0x5c;
namespace Crypto {
namespace Authentication {
template<typename HashT>
class HMAC {
public:
using HashType = HashT;
using TagType = typename HashType::DigestType;
constexpr size_t digest_size() const { return m_inner_hasher.digest_size(); }
template<typename KeyBufferType, typename... Args>
HMAC(KeyBufferType key, Args... args)
: m_inner_hasher(args...)
, m_outer_hasher(args...)
{
derive_key(key);
reset();
}
TagType process(const u8* message, size_t length)
{
reset();
update(message, length);
return digest();
}
void update(const u8* message, size_t length)
{
m_inner_hasher.update(message, length);
}
TagType process(ReadonlyBytes span) { return process(span.data(), span.size()); }
TagType process(StringView string) { return process((const u8*)string.characters_without_null_termination(), string.length()); }
void update(ReadonlyBytes span) { return update(span.data(), span.size()); }
void update(StringView string) { return update((const u8*)string.characters_without_null_termination(), string.length()); }
TagType digest()
{
m_outer_hasher.update(m_inner_hasher.digest().immutable_data(), m_inner_hasher.digest_size());
auto result = m_outer_hasher.digest();
reset();
return result;
}
void reset()
{
m_inner_hasher.reset();
m_outer_hasher.reset();
m_inner_hasher.update(m_key_data, m_inner_hasher.block_size());
m_outer_hasher.update(m_key_data + m_inner_hasher.block_size(), m_outer_hasher.block_size());
}
String class_name() const
{
StringBuilder builder;
builder.append("HMAC-");
builder.append(m_inner_hasher.class_name());
return builder.build();
}
private:
void derive_key(const u8* key, size_t length)
{
auto block_size = m_inner_hasher.block_size();
// Note: The block size of all the current hash functions is 512 bits.
Vector<u8, 64> v_key;
v_key.resize(block_size);
auto key_buffer = v_key.span();
// m_key_data is zero'd, so copying the data in
// the first few bytes leaves the rest zero, which
// is exactly what we want (zero padding)
if (length > block_size) {
m_inner_hasher.update(key, length);
auto digest = m_inner_hasher.digest();
// FIXME: should we check if the hash function creates more data than its block size?
key_buffer.overwrite(0, digest.immutable_data(), m_inner_hasher.digest_size());
} else {
key_buffer.overwrite(0, key, length);
}
// fill out the inner and outer padded keys
auto* i_key = m_key_data;
auto* o_key = m_key_data + block_size;
for (size_t i = 0; i < block_size; ++i) {
auto key_byte = key_buffer[i];
i_key[i] = key_byte ^ IPAD;
o_key[i] = key_byte ^ OPAD;
}
}
void derive_key(ReadonlyBytes key) { derive_key(key.data(), key.size()); }
void derive_key(StringView key) { derive_key(key.bytes()); }
HashType m_inner_hasher, m_outer_hasher;
u8 m_key_data[2048];
};
}
}