more detailed NSS error report
[LibreOffice.git] / comphelper / source / misc / hash.cxx
blob9fab6028a65910eae35373081fc4484949745a1c
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::SHA512:
47 return HASH_AlgSHA512;
50 return HASH_AlgNULL;
52 #elif USE_TLS_OPENSSL
53 EVP_MD_CTX* mpContext;
55 const EVP_MD* getOpenSSLType() const
57 switch (meType)
59 case HashType::MD5:
60 return EVP_md5();
61 case HashType::SHA1:
62 return EVP_sha1();
63 case HashType::SHA256:
64 return EVP_sha256();
65 case HashType::SHA512:
66 return EVP_sha512();
69 return nullptr;
71 #endif
73 HashType const meType;
75 HashImpl(HashType eType):
76 meType(eType)
79 #if USE_TLS_NSS
80 auto const e = NSS_NoDB_Init(nullptr);
81 if (e != SECSuccess) {
82 PRErrorCode error = PR_GetError();
83 const char* errorText = PR_ErrorToName(error);
84 throw css::uno::RuntimeException("NSS_NoDB_Init failed with " + OUString(errorText, strlen(errorText), RTL_TEXTENCODING_UTF8) + " (" + OUString::number((int) error) + ")");
86 mpContext = HASH_Create(getNSSType());
87 HASH_Begin(mpContext);
88 #elif USE_TLS_OPENSSL
89 mpContext = EVP_MD_CTX_create();
90 EVP_DigestInit_ex(mpContext, getOpenSSLType(), nullptr);
91 #endif
94 ~HashImpl()
96 #if USE_TLS_NSS
97 HASH_Destroy(mpContext);
98 #elif USE_TLS_OPENSSL
99 EVP_MD_CTX_destroy(mpContext);
100 #endif
104 Hash::Hash(HashType eType):
105 mpImpl(new HashImpl(eType))
109 Hash::~Hash()
113 void Hash::update(const unsigned char* pInput, size_t length)
115 #if USE_TLS_NSS
116 HASH_Update(mpImpl->mpContext, pInput, length);
117 #elif USE_TLS_OPENSSL
118 EVP_DigestUpdate(mpImpl->mpContext, pInput, length);
119 #else
120 (void)pInput;
121 (void)length;
122 #endif
125 std::vector<unsigned char> Hash::finalize()
127 std::vector<unsigned char> hash(getLength(), 0);
128 unsigned int digestWrittenLength;
129 #if USE_TLS_NSS
130 HASH_End(mpImpl->mpContext, hash.data(), &digestWrittenLength, getLength());
131 #elif USE_TLS_OPENSSL
132 EVP_DigestFinal_ex(mpImpl->mpContext, hash.data(), &digestWrittenLength);
133 #else
134 (void)digestWrittenLength;
135 #endif
137 return hash;
140 size_t Hash::getLength() const
142 switch (mpImpl->meType)
144 case HashType::MD5:
145 return MD5_HASH_LENGTH;
146 case HashType::SHA1:
147 return SHA1_HASH_LENGTH;
148 case HashType::SHA256:
149 return SHA256_HASH_LENGTH;
150 case HashType::SHA512:
151 return SHA512_HASH_LENGTH;
154 return 0;
157 std::vector<unsigned char> Hash::calculateHash(const unsigned char* pInput, size_t length, HashType eType)
159 Hash aHash(eType);
160 aHash.update(pInput, length);
161 return aHash.finalize();
164 std::vector<unsigned char> Hash::calculateHash(
165 const unsigned char* pInput, size_t nLength,
166 const unsigned char* pSalt, size_t nSaltLen,
167 sal_uInt32 nSpinCount,
168 IterCount eIterCount,
169 HashType eType)
171 if (!pSalt)
172 nSaltLen = 0;
174 if (!nSaltLen && !nSpinCount)
175 return calculateHash( pInput, nLength, eType);
177 Hash aHash(eType);
178 if (nSaltLen)
180 std::vector<unsigned char> initialData( nSaltLen + nLength);
181 std::copy( pSalt, pSalt + nSaltLen, initialData.begin());
182 std::copy( pInput, pInput + nLength, initialData.begin() + nSaltLen);
183 aHash.update( initialData.data(), initialData.size());
184 rtl_secureZeroMemory( initialData.data(), initialData.size());
186 else
188 aHash.update( pInput, nLength);
190 std::vector<unsigned char> hash( aHash.finalize());
192 if (nSpinCount)
194 // https://msdn.microsoft.com/en-us/library/dd920692
195 // says the iteration is concatenated after the hash.
196 // https://msdn.microsoft.com/en-us/library/dd924776 and
197 // https://msdn.microsoft.com/en-us/library/dd925430
198 // say the iteration is prepended to the hash.
199 const size_t nAddIter = (eIterCount == IterCount::NONE ? 0 : 4);
200 const size_t nIterPos = (eIterCount == IterCount::APPEND ? hash.size() : 0);
201 const size_t nHashPos = (eIterCount == IterCount::PREPEND ? nAddIter : 0);
202 std::vector<unsigned char> data( hash.size() + nAddIter, 0);
203 for (sal_uInt32 i = 0; i < nSpinCount; ++i)
205 std::copy( hash.begin(), hash.end(), data.begin() + nHashPos);
206 if (nAddIter)
208 #ifdef OSL_BIGENDIAN
209 sal_uInt32 be = i;
210 sal_uInt8* p = reinterpret_cast<sal_uInt8*>(&be);
211 std::swap( p[0], p[3] );
212 std::swap( p[1], p[2] );
213 memcpy( data.data() + nIterPos, &be, nAddIter);
214 #else
215 memcpy( data.data() + nIterPos, &i, nAddIter);
216 #endif
218 /* TODO: isn't there something better than
219 * creating/finalizing/destroying on each iteration? */
220 Hash aReHash(eType);
221 aReHash.update( data.data(), data.size());
222 hash = aReHash.finalize();
226 return hash;
229 std::vector<unsigned char> Hash::calculateHash(
230 const OUString& rPassword,
231 const std::vector<unsigned char>& rSaltValue,
232 sal_uInt32 nSpinCount,
233 IterCount eIterCount,
234 HashType eType)
236 const unsigned char* pPassBytes = reinterpret_cast<const unsigned char*>(rPassword.getStr());
237 const size_t nPassBytesLen = rPassword.getLength() * 2;
238 #ifdef OSL_BIGENDIAN
239 // Swap UTF16-BE to UTF16-LE
240 std::vector<unsigned char> vPass;
241 if (nPassBytesLen)
243 vPass.resize( nPassBytesLen);
244 std::copy( pPassBytes, pPassBytes + nPassBytesLen, vPass.begin());
245 unsigned char* p = vPass.data();
246 unsigned char const * const pEnd = p + nPassBytesLen;
247 for ( ; p < pEnd; p += 2 )
249 std::swap( p[0], p[1] );
251 pPassBytes = vPass.data();
253 #endif
254 return calculateHash( pPassBytes, nPassBytesLen, rSaltValue.data(), rSaltValue.size(), nSpinCount,
255 eIterCount, eType);
260 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */