Commit e698dfa1 authored by Dmytro Stechenko's avatar Dmytro Stechenko Committed by Facebook GitHub Bot

Add OpenSSL Hmac copy ctor/op=

Summary:
We already have this functionality for Digest part.
Let's add it for Hmac.

Reviewed By: yfeldblum

Differential Revision: D30879080

fbshipit-source-id: de6feb7aa5f6fa41f9595f00c8fa4cf3087719af
parent ba2db8d9
...@@ -16,7 +16,11 @@ ...@@ -16,7 +16,11 @@
#pragma once #pragma once
#include <cstdint>
#include <stdexcept> #include <stdexcept>
#include <utility>
#include <folly/Exception.h>
#include <folly/Range.h> #include <folly/Range.h>
#include <folly/io/IOBuf.h> #include <folly/io/IOBuf.h>
#include <folly/portability/OpenSSL.h> #include <folly/portability/OpenSSL.h>
...@@ -31,7 +35,7 @@ class OpenSSLHash { ...@@ -31,7 +35,7 @@ class OpenSSLHash {
public: public:
class Digest { class Digest {
public: public:
Digest() noexcept = default; Digest() noexcept {} // = default;
Digest(const Digest& that) { copy_impl(that); } Digest(const Digest& that) { copy_impl(that); }
...@@ -143,34 +147,96 @@ class OpenSSLHash { ...@@ -143,34 +147,96 @@ class OpenSSLHash {
class Hmac { class Hmac {
public: public:
Hmac() : ctx_(HMAC_CTX_new()) {} Hmac() noexcept {} // = default;
Hmac(const Hmac& that) { copy_impl(that); }
Hmac(Hmac&& that) noexcept { move_impl(std::move(that)); }
Hmac& operator=(const Hmac& that) {
if (this != &that) {
copy_impl(that);
}
return *this;
}
Hmac& operator=(Hmac&& that) noexcept {
if (this != &that) {
move_impl(std::move(that));
that.hash_reset();
}
return *this;
}
void hash_init(const EVP_MD* md, ByteRange key) { void hash_init(const EVP_MD* md, ByteRange key) {
md_ = md; ensure_ctx();
check_libssl_result( check_libssl_result(
1, 1,
HMAC_Init_ex(ctx_.get(), key.data(), int(key.size()), md_, nullptr)); HMAC_Init_ex(ctx_.get(), key.data(), int(key.size()), md, nullptr));
md_ = md;
} }
void hash_update(ByteRange data) { void hash_update(ByteRange data) {
if (ctx_ == nullptr) {
throw_exception<std::runtime_error>(
"hash_update() called without hash_init()");
}
check_libssl_result(1, HMAC_Update(ctx_.get(), data.data(), data.size())); check_libssl_result(1, HMAC_Update(ctx_.get(), data.data(), data.size()));
} }
void hash_update(const IOBuf& data) { void hash_update(const IOBuf& data) {
for (auto r : data) { for (auto r : data) {
hash_update(r); hash_update(r);
} }
} }
void hash_final(MutableByteRange out) { void hash_final(MutableByteRange out) {
if (ctx_ == nullptr) {
throw_exception<std::runtime_error>(
"hash_final() called without hash_init()");
}
const auto size = EVP_MD_size(md_); const auto size = EVP_MD_size(md_);
check_out_size(size_t(size), out); check_out_size(size_t(size), out);
unsigned int len = 0; unsigned int len = 0;
check_libssl_result(1, HMAC_Final(ctx_.get(), out.data(), &len)); check_libssl_result(1, HMAC_Final(ctx_.get(), out.data(), &len));
check_libssl_result(size, int(len)); check_libssl_result(size, int(len));
md_ = nullptr; hash_reset();
} }
private: private:
const EVP_MD* md_ = nullptr; const EVP_MD* md_{nullptr};
HmacCtxUniquePtr ctx_{nullptr}; HmacCtxUniquePtr ctx_{nullptr};
void ensure_ctx() {
if (ctx_ == nullptr) {
ctx_.reset(HMAC_CTX_new());
if (ctx_ == nullptr) {
throw_exception<std::runtime_error>(
"HMAC_CTX_new() returned nullptr");
}
}
}
void hash_reset() noexcept {
md_ = nullptr;
ctx_.reset(nullptr);
}
void copy_impl(const Hmac& that) {
if (that.md_ != nullptr && that.ctx_ != nullptr) {
ensure_ctx();
this->md_ = that.md_;
check_libssl_result(
1, HMAC_CTX_copy(this->ctx_.get(), that.ctx_.get()));
} else {
hash_reset();
}
}
void move_impl(Hmac&& that) noexcept {
std::swap(this->md_, that.md_);
std::swap(this->ctx_, that.ctx_);
}
}; };
static void hmac( static void hmac(
......
...@@ -250,3 +250,240 @@ TEST_F(OpenSSLHashTest, hmac_sha256) { ...@@ -250,3 +250,240 @@ TEST_F(OpenSSLHashTest, hmac_sha256) {
OpenSSLHash::hmac_sha256(range(out), key, buf); OpenSSLHash::hmac_sha256(range(out), key, buf);
EXPECT_EQ(expected, out); EXPECT_EQ(expected, out);
} }
TEST_F(OpenSSLHashTest, hmac_sha256_hashcopy) {
std::array<uint8_t, 32> expected, actual1, actual2;
constexpr StringPiece data{"foobar"};
constexpr StringPiece key{"qwerty"};
OpenSSLHash::Hmac hmac;
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data});
OpenSSLHash::Hmac copy1{hmac};
OpenSSLHash::Hmac copy2 = hmac;
hmac.hash_final(range(expected));
copy1.hash_final(range(actual1));
copy2.hash_final(range(actual2));
EXPECT_EQ(expected, actual1);
EXPECT_EQ(expected, actual2);
}
TEST_F(OpenSSLHashTest, hmac_sha256_hashmove) {
std::array<uint8_t, 32> expected, actual1, actual2;
constexpr StringPiece data{"foobar"};
constexpr StringPiece key{"qwerty"};
HMAC(
EVP_sha256(),
key.data(),
int(key.size()),
ByteRange{data}.data(),
data.size(),
expected.data(),
nullptr);
OpenSSLHash::Hmac hmac;
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data});
OpenSSLHash::Hmac copy1{std::move(hmac)};
copy1.hash_final(range(actual1));
EXPECT_EQ(expected, actual1);
hmac = OpenSSLHash::Hmac{};
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data});
OpenSSLHash::Hmac copy2 = std::move(hmac);
copy2.hash_final(range(actual2));
EXPECT_EQ(expected, actual2);
}
TEST_F(OpenSSLHashTest, hmac_sha256_hashcopy_self) {
std::array<uint8_t, 32> expected, actual;
constexpr StringPiece data{"foobar"};
constexpr StringPiece key{"qwerty"};
HMAC(
EVP_sha256(),
key.data(),
int(key.size()),
ByteRange{data}.data(),
data.size(),
expected.data(),
nullptr);
OpenSSLHash::Hmac hmac;
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data});
OpenSSLHash::Hmac* ptr = &hmac;
hmac = *ptr;
hmac.hash_final(range(actual));
EXPECT_EQ(expected, actual);
}
TEST_F(OpenSSLHashTest, hmac_sha256_hashmove_self) {
std::array<uint8_t, 32> expected, actual;
constexpr StringPiece data{"foobar"};
constexpr StringPiece key{"qwerty"};
HMAC(
EVP_sha256(),
key.data(),
int(key.size()),
ByteRange{data}.data(),
data.size(),
expected.data(),
nullptr);
OpenSSLHash::Hmac hmac;
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data});
OpenSSLHash::Hmac* ptr = &hmac;
hmac = std::move(*ptr);
hmac.hash_final(range(actual));
EXPECT_EQ(expected, actual);
}
TEST_F(OpenSSLHashTest, hmac_sha256_hashcopy_from_default_constructed) {
std::array<uint8_t, 32> expected, actual1, actual2;
constexpr StringPiece data{"foobar"};
constexpr StringPiece key{"qwerty"};
HMAC(
EVP_sha256(),
key.data(),
int(key.size()),
ByteRange{data}.data(),
data.size(),
expected.data(),
nullptr);
OpenSSLHash::Hmac hmac;
OpenSSLHash::Hmac copy1{hmac};
OpenSSLHash::Hmac copy2 = hmac;
copy1.hash_init(EVP_sha256(), {key});
copy1.hash_update({data});
copy1.hash_final(range(actual1));
EXPECT_EQ(expected, actual1);
copy2.hash_init(EVP_sha256(), {key});
copy2.hash_update({data});
copy2.hash_final(range(actual2));
EXPECT_EQ(expected, actual2);
}
TEST_F(OpenSSLHashTest, hmac_sha256_hashmove_from_default_constructed) {
std::array<uint8_t, 32> expected, actual1, actual2;
constexpr StringPiece data{"foobar"};
constexpr StringPiece key{"qwerty"};
HMAC(
EVP_sha256(),
key.data(),
int(key.size()),
ByteRange{data}.data(),
data.size(),
expected.data(),
nullptr);
OpenSSLHash::Hmac hmac1;
OpenSSLHash::Hmac copy1{std::move(hmac1)};
OpenSSLHash::Hmac hmac2;
OpenSSLHash::Hmac copy2 = std::move(hmac2);
copy1.hash_init(EVP_sha256(), {key});
copy1.hash_update({data});
copy1.hash_final(range(actual1));
EXPECT_EQ(expected, actual1);
copy2.hash_init(EVP_sha256(), {key});
copy2.hash_update({data});
copy2.hash_final(range(actual2));
EXPECT_EQ(expected, actual2);
}
TEST_F(OpenSSLHashTest, hmac_sha256_hashcopy_intermediate) {
std::array<uint8_t, 32> expected, actual1, actual2;
constexpr StringPiece data1{"foo"};
constexpr StringPiece data2{"bar"};
constexpr StringPiece key{"qwerty"};
OpenSSLHash::Hmac hmac;
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data1});
OpenSSLHash::Hmac copy1{hmac};
OpenSSLHash::Hmac copy2 = hmac;
hmac.hash_update({data2});
copy1.hash_update({data2});
copy2.hash_update({data2});
hmac.hash_final(range(expected));
copy1.hash_final(range(actual1));
copy2.hash_final(range(actual2));
EXPECT_EQ(expected, actual1);
EXPECT_EQ(expected, actual2);
}
TEST_F(OpenSSLHashTest, hmac_sha256_movecopy_intermediate) {
std::array<uint8_t, 32> expected, actual1, actual2;
constexpr StringPiece data{"foobar"};
constexpr StringPiece data1{"foo"};
constexpr StringPiece data2{"bar"};
constexpr StringPiece key{"qwerty"};
HMAC(
EVP_sha256(),
key.data(),
int(key.size()),
ByteRange{data}.data(),
data.size(),
expected.data(),
nullptr);
OpenSSLHash::Hmac hmac;
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data1});
OpenSSLHash::Hmac copy1{std::move(hmac)};
copy1.hash_update({data2});
copy1.hash_final(range(actual1));
EXPECT_EQ(expected, actual1);
hmac.hash_init(EVP_sha256(), {key});
hmac.hash_update({data1});
OpenSSLHash::Hmac copy2 = std::move(hmac);
copy2.hash_update({data2});
copy2.hash_final(range(actual2));
EXPECT_EQ(expected, actual2);
hmac.hash_init(EVP_sha256(), {key});
}
TEST_F(OpenSSLHashTest, hmac_update_without_init_throws) {
OpenSSLHash::Hmac hmac;
EXPECT_THROW(hmac.hash_update(ByteRange{}), std::runtime_error);
}
TEST_F(OpenSSLHashTest, hmac_final_without_init_throws) {
OpenSSLHash::Hmac hmac;
std::array<uint8_t, 32> out;
EXPECT_THROW(hmac.hash_final(range(out)), std::runtime_error);
}
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