Commit 5dcbf6d4 authored by Dominik Charousset's avatar Dominik Charousset

Add SHA1 hash implementation

parent ac0f23bc
...@@ -106,7 +106,6 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS} ...@@ -106,7 +106,6 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/detail/thread_safe_actor_clock.cpp src/detail/thread_safe_actor_clock.cpp
src/detail/tick_emitter.cpp src/detail/tick_emitter.cpp
src/detail/token_based_credit_controller.cpp src/detail/token_based_credit_controller.cpp
src/detail/token_based_credit_controller.cpp
src/detail/type_id_list_builder.cpp src/detail/type_id_list_builder.cpp
src/downstream_manager.cpp src/downstream_manager.cpp
src/downstream_manager_base.cpp src/downstream_manager_base.cpp
...@@ -118,6 +117,7 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS} ...@@ -118,6 +117,7 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/group.cpp src/group.cpp
src/group_manager.cpp src/group_manager.cpp
src/group_module.cpp src/group_module.cpp
src/hash/sha1.cpp
src/inbound_path.cpp src/inbound_path.cpp
src/init_global_meta_objects.cpp src/init_global_meta_objects.cpp
src/intrusive/inbox_result_strings.cpp src/intrusive/inbox_result_strings.cpp
...@@ -272,6 +272,7 @@ caf_add_test_suites(caf-core-test ...@@ -272,6 +272,7 @@ caf_add_test_suites(caf-core-test
fused_downstream_manager fused_downstream_manager
handles handles
hash.fnv hash.fnv
hash.sha1
intrusive.drr_cached_queue intrusive.drr_cached_queue
intrusive.drr_queue intrusive.drr_queue
intrusive.fifo_inbox intrusive.fifo_inbox
......
...@@ -43,8 +43,6 @@ class fnv : public save_inspector_base<fnv<T>> { ...@@ -43,8 +43,6 @@ class fnv : public save_inspector_base<fnv<T>> {
public: public:
static_assert(sizeof(T) == 4 || sizeof(T) == 8); static_assert(sizeof(T) == 4 || sizeof(T) == 8);
using super = save_inspector;
constexpr fnv() noexcept : result(init()) { constexpr fnv() noexcept : result(init()) {
// nop // nop
} }
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/byte.hpp"
#include "caf/detail/ieee_754.hpp"
#include "caf/save_inspector_base.hpp"
#include "caf/span.hpp"
#include "caf/string_view.hpp"
#include "caf/type_id.hpp"
#include <array>
namespace caf::hash {
/// US Secure Hash Algorithm 1 (SHA1) as defined in RFC 3174.
class sha1 : public save_inspector_base<sha1> {
public:
/// Hash size in bytes.
static constexpr size_t hash_size = 20;
/// Array type for storing a 160-bit hash.
using result_type = std::array<byte, hash_size>;
sha1() noexcept;
static constexpr bool has_human_readable_format() noexcept {
return false;
}
constexpr bool begin_object(string_view) {
return true;
}
constexpr bool end_object() {
return true;
}
bool begin_field(string_view) {
return true;
}
bool begin_field(string_view, bool is_present) {
return value(static_cast<uint8_t>(is_present));
}
bool begin_field(string_view, span<const type_id_t>, size_t index) {
return value(index);
}
bool begin_field(string_view, bool is_present, span<const type_id_t>,
size_t index) {
value(static_cast<uint8_t>(is_present));
if (is_present)
value(index);
return true;
}
constexpr bool end_field() {
return true;
}
constexpr bool begin_tuple(size_t) {
return true;
}
constexpr bool end_tuple() {
return true;
}
constexpr bool begin_key_value_pair() {
return true;
}
constexpr bool end_key_value_pair() {
return true;
}
constexpr bool begin_sequence(size_t) {
return true;
}
constexpr bool end_sequence() {
return true;
}
constexpr bool begin_associative_array(size_t) {
return true;
}
constexpr bool end_associative_array() {
return true;
}
template <class Integral>
std::enable_if_t<std::is_integral<Integral>::value, bool>
value(Integral x) noexcept {
auto begin = reinterpret_cast<const uint8_t*>(&x);
append(begin, begin + sizeof(Integral));
return true;
}
bool value(bool x) noexcept {
auto tmp = static_cast<uint8_t>(x);
return value(tmp);
}
bool value(float x) noexcept {
return value(detail::pack754(x));
}
bool value(double x) noexcept {
return value(detail::pack754(x));
}
bool value(string_view x) noexcept {
auto begin = reinterpret_cast<const uint8_t*>(x.data());
append(begin, begin + x.size());
return true;
}
bool value(span<const byte> x) noexcept {
auto begin = reinterpret_cast<const uint8_t*>(x.data());
append(begin, begin + x.size());
return true;
}
/// Seals this SHA-1 context and returns the 160-bit message digest.
result_type result() noexcept;
/// Convenience function for computing a SHA-1 hash value for given arguments
/// in one shot.
template <class... Ts>
static result_type compute(Ts&&... xs) noexcept {
using detail::as_mutable_ref;
sha1 f;
auto unused = f.apply_objects(xs...);
static_cast<void>(unused); // Always true.
return f.result();
}
private:
bool append(const uint8_t* begin, const uint8_t* end) noexcept;
void process_message_block();
void pad_message();
/// Stores whether `result()` has been called.
bool sealed_ = 0;
/// Stores the message digest so far.
std::array<uint32_t, hash_size / 4> intermediate_;
/// Stores the message length in bits.
uint64_t length_ = 0;
/// Stores the current index in `message_block_`.
int_least16_t message_block_index_ = 0;
/// Stores 512-bit message blocks.
std::array<uint8_t, 64> message_block_;
};
} // namespace caf::hash
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/hash/sha1.hpp"
#include <cstring>
#define SHA1CircularShift(bits, word) \
(((word) << (bits)) | ((word) >> (32 - (bits))))
namespace caf::hash {
sha1::sha1() noexcept {
intermediate_[0] = 0x67452301;
intermediate_[1] = 0xEFCDAB89;
intermediate_[2] = 0x98BADCFE;
intermediate_[3] = 0x10325476;
intermediate_[4] = 0xC3D2E1F0;
memset(message_block_.data(), 0, message_block_.size());
}
sha1::result_type sha1::result() noexcept {
if (!sealed_) {
pad_message();
memset(message_block_.data(), 0, message_block_.size());
length_ = 0;
sealed_ = true;
}
std::array<byte, sha1::hash_size> buf;
for (size_t i = 0; i < hash_size; ++i) {
auto tmp = intermediate_[i >> 2] >> 8 * (3 - (i & 0x03));
buf[i] = static_cast<byte>(tmp);
}
return buf;
}
bool sha1::append(const uint8_t* begin, const uint8_t* end) noexcept {
if (sealed_) {
emplace_error(sec::runtime_error,
"cannot append to a sealed SHA-1 context");
return false;
}
for (auto i = begin; i != end; ++i) {
if (length_ >= std::numeric_limits<uint64_t>::max() - 8) {
emplace_error(sec::runtime_error, "SHA-1 message too long");
return false;
}
message_block_[message_block_index_++] = *i;
length_ += 8;
if (message_block_index_ == 64)
process_message_block();
}
return true;
}
void sha1::process_message_block() {
const uint32_t K[] = {
0x5A827999,
0x6ED9EBA1,
0x8F1BBCDC,
0xCA62C1D6,
};
uint32_t W[80]; // Word sequence.
uint32_t A, B, C, D, E; // Word buffers.
for (auto t = 0; t < 16; t++) {
W[t] = message_block_[t * 4] << 24;
W[t] |= message_block_[t * 4 + 1] << 16;
W[t] |= message_block_[t * 4 + 2] << 8;
W[t] |= message_block_[t * 4 + 3];
}
for (auto t = 16; t < 80; t++) {
W[t] = SHA1CircularShift(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]);
}
A = intermediate_[0];
B = intermediate_[1];
C = intermediate_[2];
D = intermediate_[3];
E = intermediate_[4];
for (auto t = 0; t < 20; t++) {
auto tmp = SHA1CircularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[t]
+ K[0];
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = tmp;
}
for (auto t = 20; t < 40; t++) {
auto tmp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[1];
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = tmp;
}
for (auto t = 40; t < 60; t++) {
auto tmp = SHA1CircularShift(5, A) + ((B & C) | (B & D) | (C & D)) + E
+ W[t] + K[2];
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = tmp;
}
for (auto t = 60; t < 80; t++) {
auto tmp = SHA1CircularShift(5, A) + (B ^ C ^ D) + E + W[t] + K[3];
E = D;
D = C;
C = SHA1CircularShift(30, B);
B = A;
A = tmp;
}
intermediate_[0] += A;
intermediate_[1] += B;
intermediate_[2] += C;
intermediate_[3] += D;
intermediate_[4] += E;
message_block_index_ = 0;
}
void sha1::pad_message() {
if (message_block_index_ > 55) {
message_block_[message_block_index_++] = 0x80;
while (message_block_index_ < 64)
message_block_[message_block_index_++] = 0;
process_message_block();
while (message_block_index_ < 56)
message_block_[message_block_index_++] = 0;
} else {
message_block_[message_block_index_++] = 0x80;
while (message_block_index_ < 56)
message_block_[message_block_index_++] = 0;
}
message_block_[56] = static_cast<uint8_t>(length_ >> 56);
message_block_[57] = static_cast<uint8_t>(length_ >> 48);
message_block_[58] = static_cast<uint8_t>(length_ >> 40);
message_block_[59] = static_cast<uint8_t>(length_ >> 32);
message_block_[60] = static_cast<uint8_t>(length_ >> 24);
message_block_[61] = static_cast<uint8_t>(length_ >> 16);
message_block_[62] = static_cast<uint8_t>(length_ >> 8);
message_block_[63] = static_cast<uint8_t>(length_);
process_message_block();
}
} // namespace caf::hash
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE hash.sha1
#include "caf/hash/sha1.hpp"
#include "caf/test/dsl.hpp"
using namespace caf;
namespace {
template <class... Ts>
auto make_hash(Ts... xs) {
static_assert(sizeof...(Ts) == 20);
return hash::sha1::result_type{{static_cast<byte>(xs)...}};
}
} // namespace
#define CHECK_HASH_EQ(str, bytes) \
CAF_CHECK_EQUAL(hash::sha1::compute(string_view{str}), bytes);
CAF_TEST(strings are hashed by their content only) {
CHECK_HASH_EQ("dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11",
make_hash(0xb3, 0x7a, 0x4f, 0x2c, // Bytes 1 - 4
0xc0, 0x62, 0x4f, 0x16, // 5 - 8
0x90, 0xf6, 0x46, 0x06, // 9 - 12
0xcf, 0x38, 0x59, 0x45, // 13 - 16
0xb2, 0xbe, 0xc4, 0xea)); // 17 - 20
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment