1 | /* |
2 | * Copyright 2016-present Facebook, Inc. |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #pragma once |
18 | |
19 | #include <folly/Range.h> |
20 | #include <folly/io/IOBuf.h> |
21 | #include <folly/portability/OpenSSL.h> |
22 | #include <folly/ssl/OpenSSLPtrTypes.h> |
23 | |
24 | namespace folly { |
25 | namespace ssl { |
26 | |
27 | /// Warning: |
28 | /// These functions are not thread-safe unless you initialize OpenSSL. |
29 | class OpenSSLHash { |
30 | public: |
31 | class Digest { |
32 | public: |
33 | Digest() : ctx_(EVP_MD_CTX_new()) {} |
34 | |
35 | Digest(const Digest& other) { |
36 | ctx_ = EvpMdCtxUniquePtr(EVP_MD_CTX_new()); |
37 | if (other.md_ != nullptr) { |
38 | hash_init(other.md_); |
39 | check_libssl_result( |
40 | 1, EVP_MD_CTX_copy_ex(ctx_.get(), other.ctx_.get())); |
41 | } |
42 | } |
43 | |
44 | Digest& operator=(const Digest& other) { |
45 | this->~Digest(); |
46 | return *new (this) Digest(other); |
47 | } |
48 | |
49 | void hash_init(const EVP_MD* md) { |
50 | md_ = md; |
51 | check_libssl_result(1, EVP_DigestInit_ex(ctx_.get(), md, nullptr)); |
52 | } |
53 | void hash_update(ByteRange data) { |
54 | check_libssl_result( |
55 | 1, EVP_DigestUpdate(ctx_.get(), data.data(), data.size())); |
56 | } |
57 | void hash_update(const IOBuf& data) { |
58 | for (auto r : data) { |
59 | hash_update(r); |
60 | } |
61 | } |
62 | void hash_final(MutableByteRange out) { |
63 | const auto size = EVP_MD_size(md_); |
64 | check_out_size(size_t(size), out); |
65 | unsigned int len = 0; |
66 | check_libssl_result(1, EVP_DigestFinal_ex(ctx_.get(), out.data(), &len)); |
67 | check_libssl_result(size, int(len)); |
68 | md_ = nullptr; |
69 | } |
70 | |
71 | private: |
72 | const EVP_MD* md_ = nullptr; |
73 | EvpMdCtxUniquePtr ctx_{nullptr}; |
74 | }; |
75 | |
76 | static void hash(MutableByteRange out, const EVP_MD* md, ByteRange data) { |
77 | Digest hash; |
78 | hash.hash_init(md); |
79 | hash.hash_update(data); |
80 | hash.hash_final(out); |
81 | } |
82 | static void hash(MutableByteRange out, const EVP_MD* md, const IOBuf& data) { |
83 | Digest hash; |
84 | hash.hash_init(md); |
85 | hash.hash_update(data); |
86 | hash.hash_final(out); |
87 | } |
88 | static void sha1(MutableByteRange out, ByteRange data) { |
89 | hash(out, EVP_sha1(), data); |
90 | } |
91 | static void sha1(MutableByteRange out, const IOBuf& data) { |
92 | hash(out, EVP_sha1(), data); |
93 | } |
94 | static void sha256(MutableByteRange out, ByteRange data) { |
95 | hash(out, EVP_sha256(), data); |
96 | } |
97 | static void sha256(MutableByteRange out, const IOBuf& data) { |
98 | hash(out, EVP_sha256(), data); |
99 | } |
100 | |
101 | class Hmac { |
102 | public: |
103 | Hmac() : ctx_(HMAC_CTX_new()) {} |
104 | |
105 | void hash_init(const EVP_MD* md, ByteRange key) { |
106 | md_ = md; |
107 | check_libssl_result( |
108 | 1, |
109 | HMAC_Init_ex(ctx_.get(), key.data(), int(key.size()), md_, nullptr)); |
110 | } |
111 | void hash_update(ByteRange data) { |
112 | check_libssl_result(1, HMAC_Update(ctx_.get(), data.data(), data.size())); |
113 | } |
114 | void hash_update(const IOBuf& data) { |
115 | for (auto r : data) { |
116 | hash_update(r); |
117 | } |
118 | } |
119 | void hash_final(MutableByteRange out) { |
120 | const auto size = EVP_MD_size(md_); |
121 | check_out_size(size_t(size), out); |
122 | unsigned int len = 0; |
123 | check_libssl_result(1, HMAC_Final(ctx_.get(), out.data(), &len)); |
124 | check_libssl_result(size, int(len)); |
125 | md_ = nullptr; |
126 | } |
127 | |
128 | private: |
129 | const EVP_MD* md_ = nullptr; |
130 | HmacCtxUniquePtr ctx_{nullptr}; |
131 | }; |
132 | |
133 | static void |
134 | hmac(MutableByteRange out, const EVP_MD* md, ByteRange key, ByteRange data) { |
135 | Hmac hmac; |
136 | hmac.hash_init(md, key); |
137 | hmac.hash_update(data); |
138 | hmac.hash_final(out); |
139 | } |
140 | static void hmac( |
141 | MutableByteRange out, |
142 | const EVP_MD* md, |
143 | ByteRange key, |
144 | const IOBuf& data) { |
145 | Hmac hmac; |
146 | hmac.hash_init(md, key); |
147 | hmac.hash_update(data); |
148 | hmac.hash_final(out); |
149 | } |
150 | static void hmac_sha1(MutableByteRange out, ByteRange key, ByteRange data) { |
151 | hmac(out, EVP_sha1(), key, data); |
152 | } |
153 | static void |
154 | hmac_sha1(MutableByteRange out, ByteRange key, const IOBuf& data) { |
155 | hmac(out, EVP_sha1(), key, data); |
156 | } |
157 | static void hmac_sha256(MutableByteRange out, ByteRange key, ByteRange data) { |
158 | hmac(out, EVP_sha256(), key, data); |
159 | } |
160 | static void |
161 | hmac_sha256(MutableByteRange out, ByteRange key, const IOBuf& data) { |
162 | hmac(out, EVP_sha256(), key, data); |
163 | } |
164 | |
165 | private: |
166 | static inline void check_out_size(size_t size, MutableByteRange out) { |
167 | if (LIKELY(size == out.size())) { |
168 | return; |
169 | } |
170 | check_out_size_throw(size, out); |
171 | } |
172 | [[noreturn]] static void check_out_size_throw( |
173 | size_t size, |
174 | MutableByteRange out); |
175 | |
176 | static inline void check_libssl_result(int expected, int result) { |
177 | if (LIKELY(result == expected)) { |
178 | return; |
179 | } |
180 | throw_exception<std::runtime_error>("openssl crypto function failed" ); |
181 | } |
182 | }; |
183 | |
184 | } // namespace ssl |
185 | } // namespace folly |
186 | |