TrinityCore
Loading...
Searching...
No Matches
RSA.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "RSA.h"
19#include "HMAC.h"
20#include "Memory.h"
21#include <openssl/core_names.h>
22#include <openssl/params.h>
23#include <openssl/pem.h>
24#include <openssl/provider.h>
25#include <algorithm>
26#include <memory>
27#include <vector>
28#include <cstring>
29
30namespace
31{
32extern OSSL_DISPATCH const HMAC_SHA256_funcs[];
33extern OSSL_ALGORITHM const HMAC_SHA256_algs[];
34extern OSSL_DISPATCH const HMAC_SHA256_method[];
35
36struct HMAC_SHA256_MD
37{
38 struct CTX_DATA
39 {
41 };
42
43 HMAC_SHA256_MD()
44 {
45 _lib = OSSL_LIB_CTX_new();
46 OSSL_PROVIDER_add_builtin(_lib, "trinity-rsa-hmac-sha256", &InitProvider);
47 _handle = OSSL_PROVIDER_try_load(_lib, "trinity-rsa-hmac-sha256", 1);
48 }
49
50 ~HMAC_SHA256_MD()
51 {
52 if (_handle)
53 OSSL_PROVIDER_unload(_handle);
54 if (_lib)
55 OSSL_LIB_CTX_free(_lib);
56 }
57
58 OSSL_LIB_CTX* GetLib() const
59 {
60 return _lib;
61 }
62
63 static int InitProvider(const OSSL_CORE_HANDLE* /*handle*/, const OSSL_DISPATCH* /*in*/, const OSSL_DISPATCH** out, void** /*provctx*/)
64 {
65 *out = HMAC_SHA256_method;
66 return 1;
67 }
68
69 static OSSL_ALGORITHM const* QueryProvider(void* /*provctx*/, int operation_id, int* no_cache)
70 {
71 *no_cache = 0;
72 if (operation_id == OSSL_OP_DIGEST)
73 return HMAC_SHA256_algs;
74
75 return nullptr;
76 }
77
78 static CTX_DATA* DigestNew()
79 {
80 CTX_DATA* data = new CTX_DATA();
81 data->hmac = nullptr;
82 return data;
83 }
84
85 static int DigestInit(void* dctx, OSSL_PARAM const* params)
86 {
87 CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(dctx);
88
89 delete ctxData->hmac;
90 if (OSSL_PARAM const* keyParam = OSSL_PARAM_locate_const(params, "hmac-key"))
91 {
92 uint8 const* key = nullptr;
93 size_t keyLength = 0;
94 if (OSSL_PARAM_get_octet_ptr(keyParam, reinterpret_cast<void const**>(&key), &keyLength))
95 {
96 ctxData->hmac = new Trinity::Crypto::HMAC_SHA256(key, keyLength);
97 return 1;
98 }
99 }
100
101 return 0;
102 }
103
104 static int DigestUpdate(void* dctx, const unsigned char* in, size_t inl)
105 {
106 reinterpret_cast<CTX_DATA*>(dctx)->hmac->UpdateData(in, inl);
107 return 1;
108 }
109
110 static int DigestFinal(void* dctx, unsigned char* out, size_t* outl, size_t outsz)
111 {
112 CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(dctx);
113 ctxData->hmac->Finalize();
114 *outl = std::min(ctxData->hmac->GetDigest().size(), outsz);
115 memcpy(out, ctxData->hmac->GetDigest().data(), *outl);
116 return 1;
117 }
118
119 static void DigestFree(void* dctx)
120 {
121 CTX_DATA* data = reinterpret_cast<CTX_DATA*>(dctx);
122 if (data->hmac)
123 {
124 delete data->hmac;
125 data->hmac = nullptr;
126 }
127 delete data;
128 }
129
130 static void* DigestDup(void* dctx)
131 {
132 CTX_DATA const* ctxDataFrom = reinterpret_cast<CTX_DATA const*>(dctx);
133 CTX_DATA* ctxDataTo = DigestNew();
134 if (ctxDataFrom->hmac)
135 ctxDataTo->hmac = new Trinity::Crypto::HMAC_SHA256(*ctxDataFrom->hmac);
136
137 return ctxDataTo;
138 }
139
140 static int DigestGetParams(OSSL_PARAM params[])
141 {
142 OSSL_PARAM* p = nullptr;
143
144 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_BLOCK_SIZE);
145 if (p != nullptr && !OSSL_PARAM_set_size_t(p, SHA256_CBLOCK))
146 return 0;
147
148 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE);
149 if (p != nullptr && !OSSL_PARAM_set_size_t(p, Trinity::Crypto::Constants::SHA256_DIGEST_LENGTH_BYTES))
150 return 0;
151
152 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_XOF);
153 if (p != nullptr && !OSSL_PARAM_set_int(p, 0))
154 return 0;
155
156 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_ALGID_ABSENT);
157 if (p != nullptr && !OSSL_PARAM_set_int(p, 1))
158 return 0;
159
160 return 1;
161 }
162
163 static OSSL_PARAM const* DigestGettableParams()
164 {
165 static constexpr OSSL_PARAM Params[] =
166 {
167 OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_BLOCK_SIZE, NULL),
168 OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_SIZE, NULL),
169 OSSL_PARAM_int(OSSL_DIGEST_PARAM_XOF, NULL),
170 OSSL_PARAM_int(OSSL_DIGEST_PARAM_ALGID_ABSENT, NULL),
171 OSSL_PARAM_END
172 };
173
174 return Params;
175 }
176
177private:
178 OSSL_LIB_CTX* _lib;
179 OSSL_PROVIDER* _handle;
180} const HmacSha256Md;
181
182OSSL_DISPATCH const HMAC_SHA256_funcs[] =
183{
184 { OSSL_FUNC_DIGEST_NEWCTX, (void (*)())HMAC_SHA256_MD::DigestNew },
185 { OSSL_FUNC_DIGEST_INIT, (void (*)())HMAC_SHA256_MD::DigestInit },
186 { OSSL_FUNC_DIGEST_UPDATE, (void (*)())HMAC_SHA256_MD::DigestUpdate },
187 { OSSL_FUNC_DIGEST_FINAL, (void (*)())HMAC_SHA256_MD::DigestFinal },
188 { OSSL_FUNC_DIGEST_FREECTX, (void (*)())HMAC_SHA256_MD::DigestFree },
189 { OSSL_FUNC_DIGEST_DUPCTX, (void (*)())HMAC_SHA256_MD::DigestDup },
190 { OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)())HMAC_SHA256_MD::DigestGetParams },
191 { OSSL_FUNC_DIGEST_GETTABLE_PARAMS, (void (*)())HMAC_SHA256_MD::DigestGettableParams },
192 { 0, nullptr}
193};
194
195OSSL_ALGORITHM const HMAC_SHA256_algs[] =
196{
197 // pretend this custom HMAC_SHA256 is a regular SHA256 - openssl has a whitelist of allowed digests for RSA and HMAC_SHA256 is not on it
198 { OSSL_DIGEST_NAME_SHA2_256, "provider=trinity-rsa-hmac-sha256", HMAC_SHA256_funcs, "HMAC SHA265 \"digest\" for RSA" },
199 { nullptr, nullptr, nullptr, nullptr}
200};
201
202OSSL_DISPATCH const HMAC_SHA256_method[] =
203{
204 { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void(*)())HMAC_SHA256_MD::QueryProvider },
205 { 0, nullptr },
206};
207
208}
209
210namespace Trinity::Crypto
211{
213{
214 EVP_MD_free(md);
215}
216
217std::unique_ptr<EVP_MD, RsaSignature::DigestGenerator::EVP_MD_Deleter> RsaSignature::SHA256::GetGenerator() const
218{
219 return std::unique_ptr<EVP_MD, EVP_MD_Deleter>(EVP_MD_fetch(nullptr, OSSL_DIGEST_NAME_SHA2_256, "provider=default"));
220}
221
222OSSL_LIB_CTX* RsaSignature::SHA256::GetLib() const
223{
224 return nullptr;
225}
226
227std::unique_ptr<OSSL_PARAM[]> RsaSignature::SHA256::GetParams() const
228{
229 return nullptr;
230}
231
232std::unique_ptr<EVP_MD, RsaSignature::DigestGenerator::EVP_MD_Deleter> RsaSignature::HMAC_SHA256::GetGenerator() const
233{
234 return std::unique_ptr<EVP_MD, EVP_MD_Deleter>(EVP_MD_fetch(HmacSha256Md.GetLib(), OSSL_DIGEST_NAME_SHA2_256, "provider=trinity-rsa-hmac-sha256"));
235}
236
237OSSL_LIB_CTX* RsaSignature::HMAC_SHA256::GetLib() const
238{
239 return HmacSha256Md.GetLib();
240}
241
242std::unique_ptr<OSSL_PARAM[]> RsaSignature::HMAC_SHA256::GetParams() const
243{
244 return std::unique_ptr<OSSL_PARAM[]>(new OSSL_PARAM[2]
245 {
246 OSSL_PARAM_octet_ptr("hmac-key", const_cast<void**>(reinterpret_cast<void const* const*>(&_key)), _keyLength),
247 OSSL_PARAM_END
248 });
249}
250
251RsaSignature::RsaSignature() : _ctx(Impl::GenericHashImpl::MakeCTX())
252{
253}
254
255RsaSignature::RsaSignature(RsaSignature const& other) : _ctx(Impl::GenericHashImpl::MakeCTX())
256{
257 *this = other;
258}
259
261{
262 *this = std::move(other);
263}
264
266{
267 EVP_MD_CTX_free(_ctx);
268 EVP_PKEY_free(_key);
269}
270
272{
273 if (this == &right)
274 return *this;
275
276 EVP_MD_CTX_copy_ex(_ctx, right._ctx); // Allowed to fail if not yet initialized
277 _key = right._key; // EVP_PKEY uses reference counting internally, just copy the pointer
278 EVP_PKEY_up_ref(_key); // Bump reference count for PKEY, as every instance of this class holds two references to PKEY and destructor decrements it twice
279 return *this;
280}
281
283{
284 if (this == &right)
285 return *this;
286
287 _ctx = std::exchange(right._ctx, Impl::GenericHashImpl::MakeCTX());
288 _key = std::exchange(right._key, EVP_PKEY_new());
289 return *this;
290}
291
292bool RsaSignature::LoadKeyFromFile(std::string const& fileName)
293{
294 if (_key)
295 {
296 EVP_PKEY_free(_key);
297 _key = nullptr;
298 }
299
300 auto keyBIO = make_unique_ptr_with_deleter<&BIO_free>(BIO_new_file(fileName.c_str(), "r"));
301 if (!keyBIO)
302 return false;
303
304 _key = EVP_PKEY_new();
305 if (!PEM_read_bio_PrivateKey(keyBIO.get(), &_key, nullptr, nullptr))
306 return false;
307
308 return true;
309}
310
311bool RsaSignature::LoadKeyFromString(std::string const& keyPem)
312{
313 if (_key)
314 {
315 EVP_PKEY_free(_key);
316 _key = nullptr;
317 }
318
319 auto keyBIO = make_unique_ptr_with_deleter<&BIO_free>(BIO_new_mem_buf(
320 const_cast<char*>(keyPem.c_str()) /*api hack - this function assumes memory is readonly but lacks const modifier*/,
321 keyPem.length() + 1));
322 if (!keyBIO)
323 return false;
324
325 _key = EVP_PKEY_new();
326 if (!PEM_read_bio_PrivateKey(keyBIO.get(), &_key, nullptr, nullptr))
327 return false;
328
329 return true;
330}
331
332bool RsaSignature::Sign(uint8 const* message, std::size_t messageLength, DigestGenerator& generator, std::vector<uint8>& output)
333{
334 std::unique_ptr<EVP_MD, DigestGenerator::EVP_MD_Deleter> digestGenerator = generator.GetGenerator();
335
336 auto keyCtx = make_unique_ptr_with_deleter<&EVP_PKEY_CTX_free>(EVP_PKEY_CTX_new_from_pkey(generator.GetLib(), _key, nullptr));
337 EVP_MD_CTX_set_pkey_ctx(_ctx, keyCtx.get());
338
339 std::unique_ptr<OSSL_PARAM[]> params = generator.GetParams();
340 int result = EVP_DigestSignInit_ex(_ctx, nullptr, EVP_MD_get0_name(digestGenerator.get()), generator.GetLib(), nullptr, _key, params.get());
341
342 if (result == 0)
343 return false;
344
345 result = EVP_DigestSignUpdate(_ctx, message, messageLength);
346 if (result == 0)
347 return false;
348
349 size_t signatureLength = 0;
350 result = EVP_DigestSignFinal(_ctx, nullptr, &signatureLength);
351 if (result == 0)
352 return false;
353
354 output.resize(signatureLength);
355 result = EVP_DigestSignFinal(_ctx, output.data(), &signatureLength);
356 std::reverse(output.begin(), output.end());
357 return result != 0;
358}
359}
uint8_t uint8
Definition Define.h:156
std::unordered_set< uint32 > params[2]
virtual std::unique_ptr< OSSL_PARAM[]> GetParams() const =0
virtual std::unique_ptr< EVP_MD, EVP_MD_Deleter > GetGenerator() const =0
virtual OSSL_LIB_CTX * GetLib() const =0
bool LoadKeyFromFile(std::string const &fileName)
Definition RSA.cpp:292
RsaSignature & operator=(RsaSignature const &right)
Definition RSA.cpp:271
bool LoadKeyFromString(std::string const &keyPem)
Definition RSA.cpp:311
bool Sign(std::array< uint8, N > const &message, DigestGenerator &generator, std::vector< uint8 > &output)
Definition RSA.h:87
Trinity::Impl::GenericHMAC< EVP_sha256, Constants::SHA256_DIGEST_LENGTH_BYTES > HMAC_SHA256
Definition HMAC.h:138
static constexpr size_t SHA256_DIGEST_LENGTH_BYTES
static EVP_MD_CTX * MakeCTX() noexcept
Definition CryptoHash.h:38