TrinityCore
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/pem.h>
22#include <algorithm>
23#include <memory>
24#include <vector>
25#include <cstring>
26
27#if OPENSSL_VERSION_NUMBER >= 0x30000000L
28#include <openssl/core_names.h>
29#include <openssl/params.h>
30#include <openssl/provider.h>
31#endif
32
33namespace
34{
35#if OPENSSL_VERSION_NUMBER >= 0x30000000L
36
37extern OSSL_DISPATCH const HMAC_SHA256_funcs[];
38extern OSSL_ALGORITHM const HMAC_SHA256_algs[];
39extern OSSL_DISPATCH const HMAC_SHA256_method[];
40
41#endif
42
43struct HMAC_SHA256_MD
44{
45 struct CTX_DATA
46 {
48 };
49
50#if OPENSSL_VERSION_NUMBER < 0x30000000L
51
52 HMAC_SHA256_MD()
53 {
54 _md = EVP_MD_meth_new(NID_sha256, NID_sha256WithRSAEncryption);
55 EVP_MD_meth_set_result_size(_md, Trinity::Crypto::Constants::SHA256_DIGEST_LENGTH_BYTES);
56 EVP_MD_meth_set_flags(_md, EVP_MD_FLAG_DIGALGID_ABSENT);
57 EVP_MD_meth_set_init(_md, &Init);
58 EVP_MD_meth_set_update(_md, &UpdateData);
59 EVP_MD_meth_set_final(_md, &Finalize);
60 EVP_MD_meth_set_copy(_md, &Copy);
61 EVP_MD_meth_set_cleanup(_md, &Cleanup);
62 EVP_MD_meth_set_input_blocksize(_md, SHA256_CBLOCK);
63 EVP_MD_meth_set_app_datasize(_md, sizeof(EVP_MD*) + sizeof(CTX_DATA*));
64 }
65
66 HMAC_SHA256_MD(HMAC_SHA256_MD const&) = delete;
67 HMAC_SHA256_MD(HMAC_SHA256_MD&&) = delete;
68
69 HMAC_SHA256_MD& operator=(HMAC_SHA256_MD const&) = delete;
70 HMAC_SHA256_MD& operator=(HMAC_SHA256_MD&&) = delete;
71
72 ~HMAC_SHA256_MD()
73 {
74 EVP_MD_meth_free(_md);
75 _md = nullptr;
76 }
77
78 EVP_MD* GetMd() const
79 {
80 return _md;
81 }
82
83 static int Init(EVP_MD_CTX* ctx)
84 {
85 Cleanup(ctx);
86 return 1;
87 }
88
89 static int UpdateData(EVP_MD_CTX* ctx, const void* data, size_t count)
90 {
91 CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
92 if (!ctxData->hmac)
93 return 0;
94
95 ctxData->hmac->UpdateData(reinterpret_cast<uint8 const*>(data), count);
96 return 1;
97 }
98
99 static int Finalize(EVP_MD_CTX* ctx, unsigned char* md)
100 {
101 CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
102 if (!ctxData->hmac)
103 return 0;
104
105 ctxData->hmac->Finalize();
106 memcpy(md, ctxData->hmac->GetDigest().data(), ctxData->hmac->GetDigest().size());
107 return 1;
108 }
109
110 // post-processing after openssl memcpys from source to dest (no need to cleanup dest)
111 static int Copy(EVP_MD_CTX* to, EVP_MD_CTX const* from)
112 {
113 CTX_DATA const* ctxDataFrom = reinterpret_cast<CTX_DATA const*>(EVP_MD_CTX_md_data(from));
114 CTX_DATA* ctxDataTo = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(to));
115
116 if (ctxDataFrom->hmac)
117 ctxDataTo->hmac = new Trinity::Crypto::HMAC_SHA256(*ctxDataFrom->hmac);
118
119 return 1;
120 }
121
122 static int Cleanup(EVP_MD_CTX* ctx)
123 {
124 CTX_DATA* data = reinterpret_cast<CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
125 if (data->hmac)
126 {
127 delete data->hmac;
128 data->hmac = nullptr;
129 }
130
131 return 1;
132 }
133
134private:
135 EVP_MD* _md;
136
137#else
138
139 HMAC_SHA256_MD()
140 {
141 _lib = OSSL_LIB_CTX_new();
142 OSSL_PROVIDER_add_builtin(_lib, "trinity-rsa-hmac-sha256", &InitProvider);
143 _handle = OSSL_PROVIDER_try_load(_lib, "trinity-rsa-hmac-sha256", 1);
144 }
145
146 ~HMAC_SHA256_MD()
147 {
148 if (_handle)
149 OSSL_PROVIDER_unload(_handle);
150 if (_lib)
151 OSSL_LIB_CTX_free(_lib);
152 }
153
154 OSSL_LIB_CTX* GetLib() const
155 {
156 return _lib;
157 }
158
159 static int InitProvider(const OSSL_CORE_HANDLE* /*handle*/, const OSSL_DISPATCH* /*in*/, const OSSL_DISPATCH** out, void** /*provctx*/)
160 {
161 *out = HMAC_SHA256_method;
162 return 1;
163 }
164
165 static OSSL_ALGORITHM const* QueryProvider(void* /*provctx*/, int operation_id, int* no_cache)
166 {
167 *no_cache = 0;
168 if (operation_id == OSSL_OP_DIGEST)
169 return HMAC_SHA256_algs;
170
171 return nullptr;
172 }
173
174 static CTX_DATA* DigestNew()
175 {
176 CTX_DATA* data = new CTX_DATA();
177 data->hmac = nullptr;
178 return data;
179 }
180
181 static int DigestInit(void* dctx, OSSL_PARAM const* params)
182 {
183 CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(dctx);
184
185 delete ctxData->hmac;
186 if (OSSL_PARAM const* keyParam = OSSL_PARAM_locate_const(params, "hmac-key"))
187 {
188 uint8 const* key = nullptr;
189 size_t keyLength = 0;
190 if (OSSL_PARAM_get_octet_ptr(keyParam, reinterpret_cast<void const**>(&key), &keyLength))
191 {
192 ctxData->hmac = new Trinity::Crypto::HMAC_SHA256(key, keyLength);
193 return 1;
194 }
195 }
196
197 return 0;
198 }
199
200 static int DigestUpdate(void* dctx, const unsigned char* in, size_t inl)
201 {
202 reinterpret_cast<CTX_DATA*>(dctx)->hmac->UpdateData(in, inl);
203 return 1;
204 }
205
206 static int DigestFinal(void* dctx, unsigned char* out, size_t* outl, size_t outsz)
207 {
208 CTX_DATA* ctxData = reinterpret_cast<CTX_DATA*>(dctx);
209 ctxData->hmac->Finalize();
210 *outl = std::min(ctxData->hmac->GetDigest().size(), outsz);
211 memcpy(out, ctxData->hmac->GetDigest().data(), *outl);
212 return 1;
213 }
214
215 static void DigestFree(void* dctx)
216 {
217 CTX_DATA* data = reinterpret_cast<CTX_DATA*>(dctx);
218 if (data->hmac)
219 {
220 delete data->hmac;
221 data->hmac = nullptr;
222 }
223 delete data;
224 }
225
226 static void* DigestDup(void* dctx)
227 {
228 CTX_DATA const* ctxDataFrom = reinterpret_cast<CTX_DATA const*>(dctx);
229 CTX_DATA* ctxDataTo = DigestNew();
230 if (ctxDataFrom->hmac)
231 ctxDataTo->hmac = new Trinity::Crypto::HMAC_SHA256(*ctxDataFrom->hmac);
232
233 return ctxDataTo;
234 }
235
236 static int DigestGetParams(OSSL_PARAM params[])
237 {
238 OSSL_PARAM* p = nullptr;
239
240 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_BLOCK_SIZE);
241 if (p != nullptr && !OSSL_PARAM_set_size_t(p, SHA256_CBLOCK))
242 return 0;
243
244 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_SIZE);
245 if (p != nullptr && !OSSL_PARAM_set_size_t(p, Trinity::Crypto::Constants::SHA256_DIGEST_LENGTH_BYTES))
246 return 0;
247
248 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_XOF);
249 if (p != nullptr && !OSSL_PARAM_set_int(p, 0))
250 return 0;
251
252 p = OSSL_PARAM_locate(params, OSSL_DIGEST_PARAM_ALGID_ABSENT);
253 if (p != nullptr && !OSSL_PARAM_set_int(p, 1))
254 return 0;
255
256 return 1;
257 }
258
259 static OSSL_PARAM const* DigestGettableParams()
260 {
261 static constexpr OSSL_PARAM Params[] =
262 {
263 OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_BLOCK_SIZE, NULL),
264 OSSL_PARAM_size_t(OSSL_DIGEST_PARAM_SIZE, NULL),
265 OSSL_PARAM_int(OSSL_DIGEST_PARAM_XOF, NULL),
266 OSSL_PARAM_int(OSSL_DIGEST_PARAM_ALGID_ABSENT, NULL),
267 OSSL_PARAM_END
268 };
269
270 return Params;
271 }
272
273private:
274 OSSL_LIB_CTX* _lib;
275 OSSL_PROVIDER* _handle;
276#endif
277} const HmacSha256Md;
278
279#if OPENSSL_VERSION_NUMBER >= 0x30000000L
280
281OSSL_DISPATCH const HMAC_SHA256_funcs[] =
282{
283 { OSSL_FUNC_DIGEST_NEWCTX, (void (*)())HMAC_SHA256_MD::DigestNew },
284 { OSSL_FUNC_DIGEST_INIT, (void (*)())HMAC_SHA256_MD::DigestInit },
285 { OSSL_FUNC_DIGEST_UPDATE, (void (*)())HMAC_SHA256_MD::DigestUpdate },
286 { OSSL_FUNC_DIGEST_FINAL, (void (*)())HMAC_SHA256_MD::DigestFinal },
287 { OSSL_FUNC_DIGEST_FREECTX, (void (*)())HMAC_SHA256_MD::DigestFree },
288 { OSSL_FUNC_DIGEST_DUPCTX, (void (*)())HMAC_SHA256_MD::DigestDup },
289 { OSSL_FUNC_DIGEST_GET_PARAMS, (void (*)())HMAC_SHA256_MD::DigestGetParams },
290 { OSSL_FUNC_DIGEST_GETTABLE_PARAMS, (void (*)())HMAC_SHA256_MD::DigestGettableParams },
291 { 0, nullptr}
292};
293
294OSSL_ALGORITHM const HMAC_SHA256_algs[] =
295{
296 // 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
297 { OSSL_DIGEST_NAME_SHA2_256, "provider=trinity-rsa-hmac-sha256", HMAC_SHA256_funcs, "HMAC SHA265 \"digest\" for RSA" },
298 { nullptr, nullptr, nullptr, nullptr}
299};
300
301OSSL_DISPATCH const HMAC_SHA256_method[] =
302{
303 { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void(*)())HMAC_SHA256_MD::QueryProvider },
304 { 0, nullptr },
305};
306#endif
307
308}
309
310namespace Trinity::Crypto
311{
312#if OPENSSL_VERSION_NUMBER >= 0x30000000L
313
315{
316 EVP_MD_free(md);
317}
318
319std::unique_ptr<EVP_MD, RsaSignature::DigestGenerator::EVP_MD_Deleter> RsaSignature::SHA256::GetGenerator() const
320{
321 return std::unique_ptr<EVP_MD, EVP_MD_Deleter>(EVP_MD_fetch(nullptr, OSSL_DIGEST_NAME_SHA2_256, "provider=default"));
322}
323
324OSSL_LIB_CTX* RsaSignature::SHA256::GetLib() const
325{
326 return nullptr;
327}
328
329std::unique_ptr<OSSL_PARAM[]> RsaSignature::SHA256::GetParams() const
330{
331 return nullptr;
332}
333
334std::unique_ptr<EVP_MD, RsaSignature::DigestGenerator::EVP_MD_Deleter> RsaSignature::HMAC_SHA256::GetGenerator() const
335{
336 return std::unique_ptr<EVP_MD, EVP_MD_Deleter>(EVP_MD_fetch(HmacSha256Md.GetLib(), OSSL_DIGEST_NAME_SHA2_256, "provider=trinity-rsa-hmac-sha256"));
337}
338
339OSSL_LIB_CTX* RsaSignature::HMAC_SHA256::GetLib() const
340{
341 return HmacSha256Md.GetLib();
342}
343
344std::unique_ptr<OSSL_PARAM[]> RsaSignature::HMAC_SHA256::GetParams() const
345{
346 return std::unique_ptr<OSSL_PARAM[]>(new OSSL_PARAM[2]
347 {
348 OSSL_PARAM_octet_ptr("hmac-key", const_cast<void**>(reinterpret_cast<void const* const*>(&_key)), _keyLength),
349 OSSL_PARAM_END
350 });
351}
352
353#else
354
356{
357}
358
359std::unique_ptr<EVP_MD, RsaSignature::DigestGenerator::EVP_MD_Deleter> RsaSignature::SHA256::GetGenerator() const
360{
361 return std::unique_ptr<EVP_MD, EVP_MD_Deleter>(const_cast<EVP_MD*>(EVP_sha256()));
362}
363
365{
366}
367
368std::unique_ptr<EVP_MD, RsaSignature::DigestGenerator::EVP_MD_Deleter> RsaSignature::HMAC_SHA256::GetGenerator() const
369{
370 return std::unique_ptr<EVP_MD, EVP_MD_Deleter>(HmacSha256Md.GetMd());
371}
372
374{
375 HMAC_SHA256_MD::CTX_DATA* ctxData = reinterpret_cast<HMAC_SHA256_MD::CTX_DATA*>(EVP_MD_CTX_md_data(ctx));
376
377 delete ctxData->hmac;
378 ctxData->hmac = new Crypto::HMAC_SHA256(_key, _keyLength);
379}
380
381#endif
382
383RsaSignature::RsaSignature() : _ctx(Impl::GenericHashImpl::MakeCTX())
384{
385}
386
387RsaSignature::RsaSignature(RsaSignature const& other) : _ctx(Impl::GenericHashImpl::MakeCTX())
388{
389 *this = other;
390}
391
393{
394 *this = std::move(other);
395}
396
398{
399 EVP_MD_CTX_free(_ctx);
400 EVP_PKEY_free(_key);
401}
402
404{
405 if (this == &right)
406 return *this;
407
408 EVP_MD_CTX_copy_ex(_ctx, right._ctx); // Allowed to fail if not yet initialized
409 _key = right._key; // EVP_PKEY uses reference counting internally, just copy the pointer
410 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
411 return *this;
412}
413
415{
416 if (this == &right)
417 return *this;
418
419 _ctx = std::exchange(right._ctx, Impl::GenericHashImpl::MakeCTX());
420 _key = std::exchange(right._key, EVP_PKEY_new());
421 return *this;
422}
423
424bool RsaSignature::LoadKeyFromFile(std::string const& fileName)
425{
426 if (_key)
427 {
428 EVP_PKEY_free(_key);
429 _key = nullptr;
430 }
431
432 auto keyBIO = make_unique_ptr_with_deleter(BIO_new_file(fileName.c_str(), "r"), BIO_free);
433 if (!keyBIO)
434 return false;
435
436 _key = EVP_PKEY_new();
437 if (!PEM_read_bio_PrivateKey(keyBIO.get(), &_key, nullptr, nullptr))
438 return false;
439
440 return true;
441}
442
443bool RsaSignature::LoadKeyFromString(std::string const& keyPem)
444{
445 if (_key)
446 {
447 EVP_PKEY_free(_key);
448 _key = nullptr;
449 }
450
451 auto keyBIO = make_unique_ptr_with_deleter(BIO_new_mem_buf(
452 const_cast<char*>(keyPem.c_str()) /*api hack - this function assumes memory is readonly but lacks const modifier*/,
453 keyPem.length() + 1), BIO_free);
454 if (!keyBIO)
455 return false;
456
457 _key = EVP_PKEY_new();
458 if (!PEM_read_bio_PrivateKey(keyBIO.get(), &_key, nullptr, nullptr))
459 return false;
460
461 return true;
462}
463
464bool RsaSignature::Sign(uint8 const* message, std::size_t messageLength, DigestGenerator& generator, std::vector<uint8>& output)
465{
466 std::unique_ptr<EVP_MD, DigestGenerator::EVP_MD_Deleter> digestGenerator = generator.GetGenerator();
467
468#if OPENSSL_VERSION_NUMBER >= 0x30000000L
469 auto keyCtx = make_unique_ptr_with_deleter(EVP_PKEY_CTX_new_from_pkey(generator.GetLib(), _key, nullptr), EVP_PKEY_CTX_free);
470 EVP_MD_CTX_set_pkey_ctx(_ctx, keyCtx.get());
471
472 std::unique_ptr<OSSL_PARAM[]> params = generator.GetParams();
473 int result = EVP_DigestSignInit_ex(_ctx, nullptr, EVP_MD_get0_name(digestGenerator.get()), generator.GetLib(), nullptr, _key, params.get());
474#else
475 int result = EVP_DigestSignInit(_ctx, nullptr, digestGenerator.get(), nullptr, _key);
477#endif
478 if (result == 0)
479 return false;
480
481 result = EVP_DigestSignUpdate(_ctx, message, messageLength);
482 if (result == 0)
483 return false;
484
485 size_t signatureLength = 0;
486 result = EVP_DigestSignFinal(_ctx, nullptr, &signatureLength);
487 if (result == 0)
488 return false;
489
490 output.resize(signatureLength);
491 result = EVP_DigestSignFinal(_ctx, output.data(), &signatureLength);
492 std::reverse(output.begin(), output.end());
493 return result != 0;
494}
495}
uint8_t uint8
Definition: Define.h:144
std::unordered_set< uint32 > params[2]
Definition: DisableMgr.cpp:50
virtual std::unique_ptr< EVP_MD, EVP_MD_Deleter > GetGenerator() const =0
virtual void PostInitCustomizeContext(EVP_MD_CTX *ctx)=0
void PostInitCustomizeContext(EVP_MD_CTX *ctx) override
Definition: RSA.cpp:373
std::unique_ptr< EVP_MD, EVP_MD_Deleter > GetGenerator() const override
Definition: RSA.cpp:368
void PostInitCustomizeContext(EVP_MD_CTX *ctx) override
Definition: RSA.cpp:364
std::unique_ptr< EVP_MD, EVP_MD_Deleter > GetGenerator() const override
Definition: RSA.cpp:359
bool LoadKeyFromFile(std::string const &fileName)
Definition: RSA.cpp:424
RsaSignature & operator=(RsaSignature const &right)
Definition: RSA.cpp:403
bool LoadKeyFromString(std::string const &keyPem)
Definition: RSA.cpp:443
bool Sign(std::array< uint8, N > const &message, DigestGenerator &generator, std::vector< uint8 > &output)
Definition: RSA.h:99
Trinity::Impl::GenericHMAC< EVP_sha256, Constants::SHA256_DIGEST_LENGTH_BYTES > HMAC_SHA256
Definition: HMAC.h:138
TC_COMMON_API void Init()
Definition: Locales.cpp:28
auto make_unique_ptr_with_deleter(T ptr, Del &&deleter)
Definition: Memory.h:41
static constexpr size_t SHA256_DIGEST_LENGTH_BYTES
static EVP_MD_CTX * MakeCTX() noexcept
Definition: CryptoHash.h:38