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
27struct 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
118StdSha1::StdSha1() : impl(new Impl()) {}
119StdSha1::~StdSha1() = default;
120
121void StdSha1::Update(const void *const buffer, const std::size_t len) { impl->Update(buffer, len); }
122void StdSha1::GetHash(void *const result) { impl->GetHash(result); }
123void StdSha1::Reset() { impl->Reset(); }
124