| 1 | /* |
| 2 | * LegacyClonk |
| 3 | * |
| 4 | * Copyright (c) 2017-2019, The LegacyClonk Team and contributors |
| 5 | * |
| 6 | * Distributed under the terms of the ISC license; see accompanying file |
| 7 | * "COPYING" for details. |
| 8 | * |
| 9 | * "Clonk" is a registered trademark of Matthes Bender, used with permission. |
| 10 | * See accompanying file "TRADEMARK" for details. |
| 11 | * |
| 12 | * To redistribute this file separately, substitute the full license texts |
| 13 | * for the above references. |
| 14 | */ |
| 15 | |
| 16 | // SHA-1 calculation using OpenSSL |
| 17 | |
| 18 | #include <Standard.h> |
| 19 | #include <StdHelpers.h> |
| 20 | #include <StdSha1.h> |
| 21 | |
| 22 | #include <openssl/err.h> |
| 23 | #include <openssl/evp.h> |
| 24 | |
| 25 | #include <stdexcept> |
| 26 | |
| 27 | struct StdSha1::Impl |
| 28 | { |
| 29 | inline static C4DeleterFunctionUniquePtr<EVP_MD_free> sha1Digest{EVP_MD_fetch(ctx: nullptr, algorithm: "SHA1" , properties: nullptr)}; |
| 30 | |
| 31 | bool isCtxValid; |
| 32 | C4DeleterFunctionUniquePtr<EVP_MD_CTX_free> ctx{EVP_MD_CTX_new()}; |
| 33 | |
| 34 | Impl() |
| 35 | { |
| 36 | Init(); |
| 37 | } |
| 38 | |
| 39 | ~Impl() |
| 40 | { |
| 41 | ClearNoExcept(); |
| 42 | } |
| 43 | |
| 44 | void Init() |
| 45 | { |
| 46 | ThrowIfFailed(function: "EVP_MD_fetch for SHA1" , success: sha1Digest != nullptr); |
| 47 | ThrowIfFailed(function: "EVP_DigestInit_ex" , result: EVP_DigestInit_ex(ctx: ctx.get(), type: sha1Digest.get(), impl: nullptr)); |
| 48 | isCtxValid = true; |
| 49 | } |
| 50 | |
| 51 | void Clear() |
| 52 | { |
| 53 | if (isCtxValid) |
| 54 | { |
| 55 | ThrowIfFailed(function: "EVP_MD_CTX_reset" , result: EVP_MD_CTX_reset(ctx: ctx.get())); |
| 56 | isCtxValid = false; |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | void ClearNoExcept() noexcept |
| 61 | { |
| 62 | try |
| 63 | { |
| 64 | Clear(); |
| 65 | } |
| 66 | catch (const std::runtime_error &) |
| 67 | { |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | void Update(const void *const buffer, const std::size_t len) |
| 72 | { |
| 73 | ThrowIfFailed(function: "EVP_DigestUpdate" , result: EVP_DigestUpdate(ctx: ctx.get(), d: buffer, cnt: len)); |
| 74 | } |
| 75 | |
| 76 | void GetHash(void *const result) |
| 77 | { |
| 78 | ThrowIfFailed(function: "EVP_DigestFinal_ex" , result: EVP_DigestFinal_ex(ctx: ctx.get(), md: static_cast<unsigned char *>(result), s: nullptr)); |
| 79 | } |
| 80 | |
| 81 | void Reset() |
| 82 | { |
| 83 | Clear(); |
| 84 | Init(); |
| 85 | } |
| 86 | |
| 87 | void ThrowIfFailed(const std::string_view function, const int result) const |
| 88 | { |
| 89 | ThrowIfFailed(function, success: result == 1); |
| 90 | } |
| 91 | |
| 92 | void ThrowIfFailed(const std::string_view function, const bool success) const |
| 93 | { |
| 94 | if (success) |
| 95 | { |
| 96 | return; |
| 97 | } |
| 98 | |
| 99 | std::string message{function}; |
| 100 | message += " failed: " ; |
| 101 | |
| 102 | C4DeleterFunctionUniquePtr<BIO_free> bio{BIO_new(type: BIO_s_mem())}; |
| 103 | if (!bio) |
| 104 | { |
| 105 | throw std::runtime_error{message + "BIO_new failed too; Further error information is unavailable" }; |
| 106 | } |
| 107 | |
| 108 | ERR_print_errors(bp: bio.get()); |
| 109 | |
| 110 | char* data; |
| 111 | std::size_t len = BIO_get_mem_data(bio.get(), &data); |
| 112 | message += std::string_view{data, len}; |
| 113 | |
| 114 | throw std::runtime_error{message}; |
| 115 | } |
| 116 | }; |
| 117 | |
| 118 | StdSha1::StdSha1() : impl(new Impl()) {} |
| 119 | StdSha1::~StdSha1() = default; |
| 120 | |
| 121 | void StdSha1::Update(const void *const buffer, const std::size_t len) { impl->Update(buffer, len); } |
| 122 | void StdSha1::GetHash(void *const result) { impl->GetHash(result); } |
| 123 | void StdSha1::Reset() { impl->Reset(); } |
| 124 | |