Coverage Report

Created: 2026-02-14 09:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/comphelper/source/misc/hash.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 */
9
10
#include <sal/config.h>
11
12
#include <com/sun/star/uno/RuntimeException.hpp>
13
#include <comphelper/hash.hxx>
14
#include <rtl/ustring.hxx>
15
#include <rtl/alloc.h>
16
#include <osl/endian.h>
17
#include <config_oox.h>
18
#include <sstream>
19
#include <iomanip>
20
21
#if USE_TLS_NSS
22
#include <nss.h>
23
#include <nspr.h>
24
#include <sechash.h>
25
#elif USE_TLS_OPENSSL
26
#include <openssl/evp.h>
27
#include <openssl/sha.h>
28
#endif // USE_TLS_OPENSSL
29
30
namespace comphelper {
31
32
std::string hashToString(const std::vector<unsigned char>& rHash)
33
0
{
34
0
    std::stringstream aStringStream;
35
0
    for (auto& i: rHash)
36
0
    {
37
0
        aStringStream << std::setw(2) << std::setfill('0') << std::hex << int(i);
38
0
    }
39
40
0
    return aStringStream.str();
41
0
}
42
43
struct HashImpl
44
{
45
46
#if USE_TLS_NSS
47
    HASHContext* mpContext;
48
49
    HASH_HashType getNSSType() const
50
    {
51
        switch (meType)
52
        {
53
            case HashType::MD5:
54
                return HASH_AlgMD5;
55
            case HashType::SHA1:
56
                return HASH_AlgSHA1;
57
            case HashType::SHA256:
58
                return HASH_AlgSHA256;
59
            case HashType::SHA384:
60
                return HASH_AlgSHA384;
61
            case HashType::SHA512:
62
                return HASH_AlgSHA512;
63
        }
64
65
        return HASH_AlgNULL;
66
    }
67
#elif USE_TLS_OPENSSL
68
    EVP_MD_CTX* mpContext;
69
70
    const EVP_MD* getOpenSSLType() const
71
2.16M
    {
72
2.16M
        switch (meType)
73
2.16M
        {
74
9.86k
            case HashType::MD5:
75
9.86k
                return EVP_md5();
76
2.15M
            case HashType::SHA1:
77
2.15M
                return EVP_sha1();
78
48
            case HashType::SHA256:
79
48
                return EVP_sha256();
80
0
            case HashType::SHA384:
81
0
                return EVP_sha384();
82
0
            case HashType::SHA512:
83
0
                return EVP_sha512();
84
2.16M
        }
85
86
0
        return nullptr;
87
2.16M
    }
88
#endif
89
90
    HashType const meType;
91
92
    HashImpl(HashType eType):
93
2.15M
        meType(eType)
94
2.15M
    {
95
96
#if USE_TLS_NSS
97
        if (!NSS_IsInitialized())
98
        {
99
            auto const e = NSS_NoDB_Init(nullptr);
100
            if (e != SECSuccess)
101
            {
102
                PRErrorCode error = PR_GetError();
103
                const char* errorText = PR_ErrorToName(error);
104
                throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + OUString::number(static_cast<int>(error)) + ")");
105
            }
106
        }
107
        mpContext = HASH_Create(getNSSType());
108
#elif USE_TLS_OPENSSL
109
2.15M
        mpContext = EVP_MD_CTX_create();
110
2.15M
#endif
111
112
2.15M
        initialize();
113
2.15M
    }
114
115
    void initialize()
116
2.16M
    {
117
#if USE_TLS_NSS
118
        HASH_Begin(mpContext);
119
#elif USE_TLS_OPENSSL
120
        EVP_DigestInit_ex(mpContext, getOpenSSLType(), nullptr);
121
2.16M
#endif
122
2.16M
    }
123
124
    ~HashImpl()
125
2.15M
    {
126
#if USE_TLS_NSS
127
        HASH_Destroy(mpContext);
128
#elif USE_TLS_OPENSSL
129
2.15M
        EVP_MD_CTX_destroy(mpContext);
130
2.15M
#endif
131
2.15M
    }
132
};
133
134
Hash::Hash(HashType eType):
135
2.15M
    mpImpl(new HashImpl(eType))
136
2.15M
{
137
2.15M
}
138
139
Hash::~Hash()
140
2.15M
{
141
2.15M
}
142
143
void Hash::update(const void* pInput, size_t length)
144
2.70M
{
145
#if USE_TLS_NSS
146
    HASH_Update(mpImpl->mpContext, static_cast<const unsigned char*>(pInput), length);
147
#elif USE_TLS_OPENSSL
148
    EVP_DigestUpdate(mpImpl->mpContext, pInput, length);
149
#else
150
    (void)pInput;
151
    (void)length;
152
#endif
153
2.70M
}
154
155
void Hash::initialize()
156
3.28k
{
157
3.28k
    mpImpl->initialize();
158
3.28k
}
159
160
std::vector<unsigned char> Hash::finalize()
161
2.15M
{
162
2.15M
    std::vector<unsigned char> hash(getLength(), 0);
163
2.15M
    unsigned int digestWrittenLength;
164
#if USE_TLS_NSS
165
    HASH_End(mpImpl->mpContext, hash.data(), &digestWrittenLength, getLength());
166
#elif USE_TLS_OPENSSL
167
    EVP_DigestFinal_ex(mpImpl->mpContext, hash.data(), &digestWrittenLength);
168
#else
169
    (void)digestWrittenLength;
170
#endif
171
172
2.15M
    return hash;
173
2.15M
}
174
175
size_t Hash::getLength() const
176
2.15M
{
177
2.15M
    switch (mpImpl->meType)
178
2.15M
    {
179
6.57k
        case HashType::MD5:
180
6.57k
            return MD5_HASH_LENGTH;
181
2.15M
        case HashType::SHA1:
182
2.15M
            return SHA1_HASH_LENGTH;
183
48
        case HashType::SHA256:
184
48
            return SHA256_HASH_LENGTH;
185
0
        case HashType::SHA384:
186
0
            return SHA384_HASH_LENGTH;
187
0
        case HashType::SHA512:
188
0
            return SHA512_HASH_LENGTH;
189
2.15M
    }
190
191
0
    return 0;
192
2.15M
}
193
194
std::vector<unsigned char> Hash::calculateHash(const void* pInput, size_t length, HashType eType)
195
1.85M
{
196
1.85M
    Hash aHash(eType);
197
1.85M
    aHash.update(pInput, length);
198
1.85M
    return aHash.finalize();
199
1.85M
}
200
201
std::vector<unsigned char> Hash::calculateHash(
202
        const void* pInput, size_t nLength,
203
        const void* pSalt, size_t nSaltLen,
204
        sal_uInt32 nSpinCount,
205
        IterCount eIterCount,
206
        HashType eType)
207
3
{
208
3
    if (!pSalt)
209
0
        nSaltLen = 0;
210
211
3
    if (!nSaltLen && !nSpinCount)
212
0
        return calculateHash( pInput, nLength, eType);
213
214
3
    Hash aHash(eType);
215
3
    if (nSaltLen)
216
3
    {
217
3
        std::vector<unsigned char> initialData( nSaltLen + nLength);
218
3
        std::copy_n(static_cast<const unsigned char*>(pSalt), nSaltLen, initialData.begin());
219
3
        std::copy_n(static_cast<const unsigned char*>(pInput), nLength, initialData.begin() + nSaltLen);
220
3
        aHash.update( initialData.data(), initialData.size());
221
3
        rtl_secureZeroMemory( initialData.data(), initialData.size());
222
3
    }
223
0
    else
224
0
    {
225
0
        aHash.update( pInput, nLength);
226
0
    }
227
3
    std::vector<unsigned char> hash( aHash.finalize());
228
229
3
    if (nSpinCount)
230
3
    {
231
        // https://msdn.microsoft.com/en-us/library/dd920692
232
        // says the iteration is concatenated after the hash.
233
        // https://msdn.microsoft.com/en-us/library/dd924776 and
234
        // https://msdn.microsoft.com/en-us/library/dd925430
235
        // say the iteration is prepended to the hash.
236
3
        const size_t nAddIter = (eIterCount == IterCount::NONE ? 0 : 4);
237
3
        const size_t nIterPos = (eIterCount == IterCount::APPEND ? hash.size() : 0);
238
3
        const size_t nHashPos = (eIterCount == IterCount::PREPEND ? nAddIter : 0);
239
3
        std::vector<unsigned char> data( hash.size() + nAddIter, 0);
240
300k
        for (sal_uInt32 i = 0; i < nSpinCount; ++i)
241
300k
        {
242
300k
            std::copy( hash.begin(), hash.end(), data.begin() + nHashPos);
243
300k
            if (nAddIter)
244
300k
            {
245
#ifdef OSL_BIGENDIAN
246
                sal_uInt32 be = OSL_SWAPDWORD(i);
247
                memcpy( data.data() + nIterPos, &be, nAddIter);
248
#else
249
300k
                memcpy( data.data() + nIterPos, &i, nAddIter);
250
300k
#endif
251
300k
            }
252
            /* TODO: isn't there something better than
253
             * creating/finalizing/destroying on each iteration? */
254
300k
            Hash aReHash(eType);
255
300k
            aReHash.update( data.data(), data.size());
256
300k
            hash = aReHash.finalize();
257
300k
        }
258
3
    }
259
260
3
    return hash;
261
3
}
262
263
std::vector<unsigned char> Hash::calculateHash(
264
        std::u16string_view rPassword,
265
        const std::vector<unsigned char>& rSaltValue,
266
        sal_uInt32 nSpinCount,
267
        IterCount eIterCount,
268
        HashType eType)
269
3
{
270
3
    const void* pPassBytes = rPassword.data();
271
3
    const size_t nPassBytesLen = rPassword.length() * 2;
272
#ifdef OSL_BIGENDIAN
273
    // Swap UTF16-BE to UTF16-LE
274
    std::vector<char16_t> vPass;
275
    if (nPassBytesLen)
276
    {
277
        vPass.insert(vPass.begin(), rPassword.begin(), rPassword.end());
278
        for (char16_t& ch : vPass)
279
            ch = OSL_SWAPWORD(ch);
280
        pPassBytes = vPass.data();
281
    }
282
#endif
283
3
    return calculateHash( pPassBytes, nPassBytesLen, rSaltValue.data(), rSaltValue.size(), nSpinCount,
284
3
            eIterCount, eType);
285
3
}
286
287
}
288
289
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */