/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: */ |