Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / comphelper / source / misc / hash.cxx
blob25b93ad87e542a4a2209a7243aedc3358b0517f7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <sal/config.h>
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>
19 #if USE_TLS_NSS
20 #include <nss.h>
21 #include <nspr.h>
22 #include <sechash.h>
23 #elif USE_TLS_OPENSSL
24 #include <openssl/evp.h>
25 #include <openssl/sha.h>
26 #endif // USE_TLS_OPENSSL
28 namespace comphelper {
30 struct HashImpl
33 #if USE_TLS_NSS
34 HASHContext* mpContext;
36 HASH_HashType getNSSType() const
38 switch (meType)
40 case HashType::MD5:
41 return HASH_AlgMD5;
42 case HashType::SHA1:
43 return HASH_AlgSHA1;
44 case HashType::SHA256:
45 return HASH_AlgSHA256;
46 case HashType::SHA384:
47 return HASH_AlgSHA384;
48 case HashType::SHA512:
49 return HASH_AlgSHA512;
52 return HASH_AlgNULL;
54 #elif USE_TLS_OPENSSL
55 EVP_MD_CTX* mpContext;
57 const EVP_MD* getOpenSSLType() const
59 switch (meType)
61 case HashType::MD5:
62 return EVP_md5();
63 case HashType::SHA1:
64 return EVP_sha1();
65 case HashType::SHA256:
66 return EVP_sha256();
67 case HashType::SHA384:
68 return EVP_sha384();
69 case HashType::SHA512:
70 return EVP_sha512();
73 return nullptr;
75 #endif
77 HashType const meType;
79 HashImpl(HashType eType):
80 meType(eType)
83 #if USE_TLS_NSS
84 if (!NSS_IsInitialized())
86 auto const e = NSS_NoDB_Init(nullptr);
87 if (e != SECSuccess)
89 PRErrorCode error = PR_GetError();
90 const char* errorText = PR_ErrorToName(error);
91 throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + OUString::number(static_cast<int>(error)) + ")");
94 mpContext = HASH_Create(getNSSType());
95 HASH_Begin(mpContext);
96 #elif USE_TLS_OPENSSL
97 mpContext = EVP_MD_CTX_create();
98 EVP_DigestInit_ex(mpContext, getOpenSSLType(), nullptr);
99 #endif
102 ~HashImpl()
104 #if USE_TLS_NSS
105 HASH_Destroy(mpContext);
106 #elif USE_TLS_OPENSSL
107 EVP_MD_CTX_destroy(mpContext);
108 #endif
112 Hash::Hash(HashType eType):
113 mpImpl(new HashImpl(eType))
117 Hash::~Hash()
121 void Hash::update(const unsigned char* pInput, size_t length)
123 #if USE_TLS_NSS
124 HASH_Update(mpImpl->mpContext, pInput, length);
125 #elif USE_TLS_OPENSSL
126 EVP_DigestUpdate(mpImpl->mpContext, pInput, length);
127 #else
128 (void)pInput;
129 (void)length;
130 #endif
133 std::vector<unsigned char> Hash::finalize()
135 std::vector<unsigned char> hash(getLength(), 0);
136 unsigned int digestWrittenLength;
137 #if USE_TLS_NSS
138 HASH_End(mpImpl->mpContext, hash.data(), &digestWrittenLength, getLength());
139 #elif USE_TLS_OPENSSL
140 EVP_DigestFinal_ex(mpImpl->mpContext, hash.data(), &digestWrittenLength);
141 #else
142 (void)digestWrittenLength;
143 #endif
145 return hash;
148 size_t Hash::getLength() const
150 switch (mpImpl->meType)
152 case HashType::MD5:
153 return MD5_HASH_LENGTH;
154 case HashType::SHA1:
155 return SHA1_HASH_LENGTH;
156 case HashType::SHA256:
157 return SHA256_HASH_LENGTH;
158 case HashType::SHA384:
159 return SHA384_HASH_LENGTH;
160 case HashType::SHA512:
161 return SHA512_HASH_LENGTH;
164 return 0;
167 std::vector<unsigned char> Hash::calculateHash(const unsigned char* pInput, size_t length, HashType eType)
169 Hash aHash(eType);
170 aHash.update(pInput, length);
171 return aHash.finalize();
174 std::vector<unsigned char> Hash::calculateHash(
175 const unsigned char* pInput, size_t nLength,
176 const unsigned char* pSalt, size_t nSaltLen,
177 sal_uInt32 nSpinCount,
178 IterCount eIterCount,
179 HashType eType)
181 if (!pSalt)
182 nSaltLen = 0;
184 if (!nSaltLen && !nSpinCount)
185 return calculateHash( pInput, nLength, eType);
187 Hash aHash(eType);
188 if (nSaltLen)
190 std::vector<unsigned char> initialData( nSaltLen + nLength);
191 std::copy( pSalt, pSalt + nSaltLen, initialData.begin());
192 std::copy( pInput, pInput + nLength, initialData.begin() + nSaltLen);
193 aHash.update( initialData.data(), initialData.size());
194 rtl_secureZeroMemory( initialData.data(), initialData.size());
196 else
198 aHash.update( pInput, nLength);
200 std::vector<unsigned char> hash( aHash.finalize());
202 if (nSpinCount)
204 // https://msdn.microsoft.com/en-us/library/dd920692
205 // says the iteration is concatenated after the hash.
206 // https://msdn.microsoft.com/en-us/library/dd924776 and
207 // https://msdn.microsoft.com/en-us/library/dd925430
208 // say the iteration is prepended to the hash.
209 const size_t nAddIter = (eIterCount == IterCount::NONE ? 0 : 4);
210 const size_t nIterPos = (eIterCount == IterCount::APPEND ? hash.size() : 0);
211 const size_t nHashPos = (eIterCount == IterCount::PREPEND ? nAddIter : 0);
212 std::vector<unsigned char> data( hash.size() + nAddIter, 0);
213 for (sal_uInt32 i = 0; i < nSpinCount; ++i)
215 std::copy( hash.begin(), hash.end(), data.begin() + nHashPos);
216 if (nAddIter)
218 #ifdef OSL_BIGENDIAN
219 sal_uInt32 be = i;
220 sal_uInt8* p = reinterpret_cast<sal_uInt8*>(&be);
221 std::swap( p[0], p[3] );
222 std::swap( p[1], p[2] );
223 memcpy( data.data() + nIterPos, &be, nAddIter);
224 #else
225 memcpy( data.data() + nIterPos, &i, nAddIter);
226 #endif
228 /* TODO: isn't there something better than
229 * creating/finalizing/destroying on each iteration? */
230 Hash aReHash(eType);
231 aReHash.update( data.data(), data.size());
232 hash = aReHash.finalize();
236 return hash;
239 std::vector<unsigned char> Hash::calculateHash(
240 const OUString& rPassword,
241 const std::vector<unsigned char>& rSaltValue,
242 sal_uInt32 nSpinCount,
243 IterCount eIterCount,
244 HashType eType)
246 const unsigned char* pPassBytes = reinterpret_cast<const unsigned char*>(rPassword.getStr());
247 const size_t nPassBytesLen = rPassword.getLength() * 2;
248 #ifdef OSL_BIGENDIAN
249 // Swap UTF16-BE to UTF16-LE
250 std::vector<unsigned char> vPass;
251 if (nPassBytesLen)
253 vPass.resize( nPassBytesLen);
254 std::copy( pPassBytes, pPassBytes + nPassBytesLen, vPass.begin());
255 unsigned char* p = vPass.data();
256 unsigned char const * const pEnd = p + nPassBytesLen;
257 for ( ; p < pEnd; p += 2 )
259 std::swap( p[0], p[1] );
261 pPassBytes = vPass.data();
263 #endif
264 return calculateHash( pPassBytes, nPassBytesLen, rSaltValue.data(), rSaltValue.size(), nSpinCount,
265 eIterCount, eType);
270 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */