--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/ssl/OpenSSLHash.h>
+
+#include <folly/Format.h>
+
+namespace folly {
+namespace ssl {
+
+void OpenSSLHash::check_out_size_throw(size_t size, MutableByteRange out) {
+ throw std::invalid_argument(folly::sformat(
+ "expected out of size {} but was of size {}", size, out.size()));
+}
+
+void OpenSSLHash::check_libssl_result_throw() {
+ throw std::runtime_error("openssl crypto function failed");
+}
+
+}
+}
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#include <folly/Range.h>
+#include <folly/io/IOBuf.h>
+
+namespace folly {
+namespace ssl {
+
+/// Warning:
+/// These functions are not thread-safe unless you initialize OpenSSL.
+class OpenSSLHash {
+ public:
+
+ class Digest {
+ public:
+ Digest() {
+ EVP_MD_CTX_init(&ctx_);
+ }
+ ~Digest() {
+ EVP_MD_CTX_cleanup(&ctx_);
+ }
+ void hash_init(const EVP_MD* md) {
+ md_ = md;
+ check_libssl_result(1, EVP_DigestInit_ex(&ctx_, md, nullptr));
+ }
+ void hash_update(ByteRange data) {
+ check_libssl_result(1, EVP_DigestUpdate(&ctx_, data.data(), data.size()));
+ }
+ void hash_update(const IOBuf& data) {
+ for (auto r : data) {
+ hash_update(r);
+ }
+ }
+ void hash_final(MutableByteRange out) {
+ const auto size = EVP_MD_size(md_);
+ check_out_size(size, out);
+ unsigned int len = 0;
+ check_libssl_result(1, EVP_DigestFinal_ex(&ctx_, out.data(), &len));
+ check_libssl_result(size, len);
+ md_ = nullptr;
+ }
+ private:
+ const EVP_MD* md_ = nullptr;
+ EVP_MD_CTX ctx_;
+ };
+
+ static void hash(
+ MutableByteRange out,
+ const EVP_MD* md,
+ ByteRange data) {
+ Digest hash;
+ hash.hash_init(md);
+ hash.hash_update(data);
+ hash.hash_final(out);
+ }
+ static void hash(
+ MutableByteRange out,
+ const EVP_MD* md,
+ const IOBuf& data) {
+ Digest hash;
+ hash.hash_init(md);
+ hash.hash_update(data);
+ hash.hash_final(out);
+ }
+ static void sha1(MutableByteRange out, ByteRange data) {
+ hash(out, EVP_sha1(), data);
+ }
+ static void sha1(MutableByteRange out, const IOBuf& data) {
+ hash(out, EVP_sha1(), data);
+ }
+ static void sha256(MutableByteRange out, ByteRange data) {
+ hash(out, EVP_sha256(), data);
+ }
+ static void sha256(MutableByteRange out, const IOBuf& data) {
+ hash(out, EVP_sha256(), data);
+ }
+
+ class Hmac {
+ public:
+ Hmac() {
+ HMAC_CTX_init(&ctx_);
+ }
+ ~Hmac() {
+ HMAC_CTX_cleanup(&ctx_);
+ }
+ void hash_init(const EVP_MD* md, ByteRange key) {
+ md_ = md;
+ check_libssl_result(
+ 1, HMAC_Init_ex(&ctx_, key.data(), key.size(), md_, nullptr));
+ }
+ void hash_update(ByteRange data) {
+ check_libssl_result(1, HMAC_Update(&ctx_, data.data(), data.size()));
+ }
+ void hash_update(const IOBuf& data) {
+ for (auto r : data) {
+ hash_update(r);
+ }
+ }
+ void hash_final(MutableByteRange out) {
+ const auto size = EVP_MD_size(md_);
+ check_out_size(size, out);
+ unsigned int len = 0;
+ check_libssl_result(1, HMAC_Final(&ctx_, out.data(), &len));
+ check_libssl_result(size, len);
+ md_ = nullptr;
+ }
+ private:
+ const EVP_MD* md_ = nullptr;
+ HMAC_CTX ctx_;
+ };
+
+ static void hmac(
+ MutableByteRange out,
+ const EVP_MD* md,
+ ByteRange key,
+ ByteRange data) {
+ Hmac hmac;
+ hmac.hash_init(md, key);
+ hmac.hash_update(data);
+ hmac.hash_final(out);
+ };
+ static void hmac(
+ MutableByteRange out,
+ const EVP_MD* md,
+ ByteRange key,
+ const IOBuf& data) {
+ Hmac hmac;
+ hmac.hash_init(md, key);
+ hmac.hash_update(data);
+ hmac.hash_final(out);
+ }
+ static void hmac_sha1(
+ MutableByteRange out, ByteRange key, ByteRange data) {
+ hmac(out, EVP_sha1(), key, data);
+ }
+ static void hmac_sha1(
+ MutableByteRange out, ByteRange key, const IOBuf& data) {
+ hmac(out, EVP_sha1(), key, data);
+ }
+ static void hmac_sha256(
+ MutableByteRange out, ByteRange key, ByteRange data) {
+ hmac(out, EVP_sha256(), key, data);
+ }
+ static void hmac_sha256(
+ MutableByteRange out, ByteRange key, const IOBuf& data) {
+ hmac(out, EVP_sha256(), key, data);
+ }
+
+ private:
+ static inline void check_out_size(size_t size, MutableByteRange out) {
+ if (LIKELY(size == out.size())) {
+ return;
+ }
+ check_out_size_throw(size, out);
+ }
+ static void check_out_size_throw(size_t size, MutableByteRange out);
+
+ static inline void check_libssl_result(int expected, int result) {
+ if (LIKELY(result == expected)) {
+ return;
+ }
+ check_libssl_result_throw();
+ }
+ static void check_libssl_result_throw();
+
+};
+
+}
+}
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/ssl/OpenSSLHash.h>
+
+#include <gtest/gtest.h>
+
+#include <folly/io/IOBufQueue.h>
+
+using namespace std;
+using namespace folly;
+using namespace folly::ssl;
+
+namespace {
+
+class OpenSSLHashTest : public testing::Test {};
+
+}
+
+TEST_F(OpenSSLHashTest, sha256) {
+ IOBuf buf;
+ buf.prependChain(IOBuf::wrapBuffer(ByteRange(StringPiece("foo"))));
+ buf.prependChain(IOBuf::wrapBuffer(ByteRange(StringPiece("bar"))));
+ EXPECT_EQ(3, buf.countChainElements());
+ EXPECT_EQ(6, buf.computeChainDataLength());
+
+ auto expected = vector<uint8_t>(32);
+ auto combined = ByteRange(StringPiece("foobar"));
+ SHA256(combined.data(), combined.size(), expected.data());
+
+ auto out = vector<uint8_t>(32);
+ OpenSSLHash::sha256(range(out), buf);
+ EXPECT_EQ(expected, out);
+}
+
+TEST_F(OpenSSLHashTest, hmac_sha256) {
+ auto key = ByteRange(StringPiece("qwerty"));
+
+ IOBuf buf;
+ buf.prependChain(IOBuf::wrapBuffer(ByteRange(StringPiece("foo"))));
+ buf.prependChain(IOBuf::wrapBuffer(ByteRange(StringPiece("bar"))));
+ EXPECT_EQ(3, buf.countChainElements());
+ EXPECT_EQ(6, buf.computeChainDataLength());
+
+ auto expected = vector<uint8_t>(32);
+ auto combined = ByteRange(StringPiece("foobar"));
+ HMAC(
+ EVP_sha256(),
+ key.data(), key.size(),
+ combined.data(), combined.size(),
+ expected.data(), nullptr);
+
+ auto out = vector<uint8_t>(32);
+ OpenSSLHash::hmac_sha256(range(out), key, buf);
+ EXPECT_EQ(expected, out);
+}