1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
26 #include <openssl/evp.h>
27 #include <openssl/sha.h>
28 #endif // USE_TLS_OPENSSL
30 namespace comphelper
{
32 std::string
hashToString(const std::vector
<unsigned char>& rHash
)
34 std::stringstream aStringStream
;
37 aStringStream
<< std::setw(2) << std::setfill('0') << std::hex
<< int(i
);
40 return aStringStream
.str();
47 HASHContext
* mpContext
;
49 HASH_HashType
getNSSType() const
57 case HashType::SHA256
:
58 return HASH_AlgSHA256
;
59 case HashType::SHA384
:
60 return HASH_AlgSHA384
;
61 case HashType::SHA512
:
62 return HASH_AlgSHA512
;
68 EVP_MD_CTX
* mpContext
;
70 const EVP_MD
* getOpenSSLType() const
78 case HashType::SHA256
:
80 case HashType::SHA384
:
82 case HashType::SHA512
:
90 HashType
const meType
;
92 HashImpl(HashType eType
):
97 if (!NSS_IsInitialized())
99 auto const e
= NSS_NoDB_Init(nullptr);
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
)) + ")");
107 mpContext
= HASH_Create(getNSSType());
108 HASH_Begin(mpContext
);
109 #elif USE_TLS_OPENSSL
110 mpContext
= EVP_MD_CTX_create();
111 EVP_DigestInit_ex(mpContext
, getOpenSSLType(), nullptr);
118 HASH_Destroy(mpContext
);
119 #elif USE_TLS_OPENSSL
120 EVP_MD_CTX_destroy(mpContext
);
125 Hash::Hash(HashType eType
):
126 mpImpl(new HashImpl(eType
))
134 void Hash::update(const unsigned char* pInput
, size_t length
)
137 HASH_Update(mpImpl
->mpContext
, pInput
, length
);
138 #elif USE_TLS_OPENSSL
139 EVP_DigestUpdate(mpImpl
->mpContext
, pInput
, length
);
146 std::vector
<unsigned char> Hash::finalize()
148 std::vector
<unsigned char> hash(getLength(), 0);
149 unsigned int digestWrittenLength
;
151 HASH_End(mpImpl
->mpContext
, hash
.data(), &digestWrittenLength
, getLength());
152 #elif USE_TLS_OPENSSL
153 EVP_DigestFinal_ex(mpImpl
->mpContext
, hash
.data(), &digestWrittenLength
);
155 (void)digestWrittenLength
;
161 size_t Hash::getLength() const
163 switch (mpImpl
->meType
)
166 return MD5_HASH_LENGTH
;
168 return SHA1_HASH_LENGTH
;
169 case HashType::SHA256
:
170 return SHA256_HASH_LENGTH
;
171 case HashType::SHA384
:
172 return SHA384_HASH_LENGTH
;
173 case HashType::SHA512
:
174 return SHA512_HASH_LENGTH
;
180 std::vector
<unsigned char> Hash::calculateHash(const unsigned char* pInput
, size_t length
, HashType eType
)
183 aHash
.update(pInput
, length
);
184 return aHash
.finalize();
187 std::vector
<unsigned char> Hash::calculateHash(
188 const unsigned char* pInput
, size_t nLength
,
189 const unsigned char* pSalt
, size_t nSaltLen
,
190 sal_uInt32 nSpinCount
,
191 IterCount eIterCount
,
197 if (!nSaltLen
&& !nSpinCount
)
198 return calculateHash( pInput
, nLength
, eType
);
203 std::vector
<unsigned char> initialData( nSaltLen
+ nLength
);
204 std::copy( pSalt
, pSalt
+ nSaltLen
, initialData
.begin());
205 std::copy( pInput
, pInput
+ nLength
, initialData
.begin() + nSaltLen
);
206 aHash
.update( initialData
.data(), initialData
.size());
207 rtl_secureZeroMemory( initialData
.data(), initialData
.size());
211 aHash
.update( pInput
, nLength
);
213 std::vector
<unsigned char> hash( aHash
.finalize());
217 // https://msdn.microsoft.com/en-us/library/dd920692
218 // says the iteration is concatenated after the hash.
219 // https://msdn.microsoft.com/en-us/library/dd924776 and
220 // https://msdn.microsoft.com/en-us/library/dd925430
221 // say the iteration is prepended to the hash.
222 const size_t nAddIter
= (eIterCount
== IterCount::NONE
? 0 : 4);
223 const size_t nIterPos
= (eIterCount
== IterCount::APPEND
? hash
.size() : 0);
224 const size_t nHashPos
= (eIterCount
== IterCount::PREPEND
? nAddIter
: 0);
225 std::vector
<unsigned char> data( hash
.size() + nAddIter
, 0);
226 for (sal_uInt32 i
= 0; i
< nSpinCount
; ++i
)
228 std::copy( hash
.begin(), hash
.end(), data
.begin() + nHashPos
);
233 sal_uInt8
* p
= reinterpret_cast<sal_uInt8
*>(&be
);
234 std::swap( p
[0], p
[3] );
235 std::swap( p
[1], p
[2] );
236 memcpy( data
.data() + nIterPos
, &be
, nAddIter
);
238 memcpy( data
.data() + nIterPos
, &i
, nAddIter
);
241 /* TODO: isn't there something better than
242 * creating/finalizing/destroying on each iteration? */
244 aReHash
.update( data
.data(), data
.size());
245 hash
= aReHash
.finalize();
252 std::vector
<unsigned char> Hash::calculateHash(
253 const OUString
& rPassword
,
254 const std::vector
<unsigned char>& rSaltValue
,
255 sal_uInt32 nSpinCount
,
256 IterCount eIterCount
,
259 const unsigned char* pPassBytes
= reinterpret_cast<const unsigned char*>(rPassword
.getStr());
260 const size_t nPassBytesLen
= rPassword
.getLength() * 2;
262 // Swap UTF16-BE to UTF16-LE
263 std::vector
<unsigned char> vPass
;
266 vPass
.resize( nPassBytesLen
);
267 std::copy( pPassBytes
, pPassBytes
+ nPassBytesLen
, vPass
.begin());
268 unsigned char* p
= vPass
.data();
269 unsigned char const * const pEnd
= p
+ nPassBytesLen
;
270 for ( ; p
< pEnd
; p
+= 2 )
272 std::swap( p
[0], p
[1] );
274 pPassBytes
= vPass
.data();
277 return calculateHash( pPassBytes
, nPassBytesLen
, rSaltValue
.data(), rSaltValue
.size(), nSpinCount
,
283 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */