Wrappers for some of OpenSSL's crypto hash functions
authorYedidya Feldblum <yfeldblum@fb.com>
Fri, 17 Jun 2016 01:30:25 +0000 (18:30 -0700)
committerFacebook Github Bot 5 <facebook-github-bot-5-bot@fb.com>
Fri, 17 Jun 2016 01:38:23 +0000 (18:38 -0700)
Summary:
[Folly] Wrappers for some of OpenSSL's crypto hash functions.

Wraps some of the OpenSSL crypto hash functions with variants that take `ByteRange` for input and `MutableByteRange` for output, and also variants that take `const IOBuf&` for input as well.

These are a bit nicer to use than passing pointers and lengths separately.

Reviewed By: ivmaykov

Differential Revision: D3434562

fbshipit-source-id: 3688ef11680a029b7664ac417a7781e70f9c6926

folly/Makefile.am
folly/ssl/OpenSSLHash.cpp [new file with mode: 0644]
folly/ssl/OpenSSLHash.h [new file with mode: 0644]
folly/ssl/test/OpenSSLHashTest.cpp [new file with mode: 0644]
folly/test/Makefile.am

index bfd670cdca8dd1c86bf86ab7ce876eccb3917426..08ce2f23a30d1b23e3a9d94bfb447058e3501caf 100644 (file)
@@ -294,6 +294,7 @@ nobase_follyinclude_HEADERS = \
        SpinLock.h \
        SpookyHashV1.h \
        SpookyHashV2.h \
+       ssl/OpenSSLHash.h \
        stats/BucketedTimeSeries-defs.h \
        stats/BucketedTimeSeries.h \
        stats/Histogram-defs.h \
@@ -432,6 +433,7 @@ libfolly_la_SOURCES = \
        SocketAddress.cpp \
        SpookyHashV1.cpp \
        SpookyHashV2.cpp \
+       ssl/OpenSSLHash.cpp \
        stats/Instantiations.cpp \
        Subprocess.cpp \
        ThreadCachedArena.cpp \
diff --git a/folly/ssl/OpenSSLHash.cpp b/folly/ssl/OpenSSLHash.cpp
new file mode 100644 (file)
index 0000000..9e3581a
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * 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");
+}
+
+}
+}
diff --git a/folly/ssl/OpenSSLHash.h b/folly/ssl/OpenSSLHash.h
new file mode 100644 (file)
index 0000000..e3ea65b
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * 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();
+
+};
+
+}
+}
diff --git a/folly/ssl/test/OpenSSLHashTest.cpp b/folly/ssl/test/OpenSSLHashTest.cpp
new file mode 100644 (file)
index 0000000..129cf40
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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);
+}
index 987db2ee42ef633db1dc7840881bab6ea2d8d4b1..7a39a8209b106a491e66e0e184efca4f1573d879 100644 (file)
@@ -266,4 +266,9 @@ function_test_SOURCES = FunctionTest.cpp
 function_test_LDADD = libfollytestmain.la
 TESTS += function_test
 
+ssl_test_SOURCES = \
+               ../ssl/OpenSSLHashTest.cpp
+ssl_test_LDADD = libfollytestmain.la
+TESTS += ssl_test
+
 check_PROGRAMS += $(TESTS)