xref: /core/comphelper/source/misc/hash.cxx (revision 368f2000)
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 <comphelper/hash.hxx>
11 #include <rtl/ustring.hxx>
12 #include <rtl/alloc.h>
13 #include <osl/endian.h>
14 #include <config_oox.h>
15 
16 #if USE_TLS_NSS
17 #include <nss.h>
18 #include <sechash.h>
19 #elif USE_TLS_OPENSSL
20 #include <openssl/evp.h>
21 #include <openssl/sha.h>
22 #endif // USE_TLS_OPENSSL
23 
24 namespace comphelper {
25 
26 struct HashImpl
27 {
28 
29 #if USE_TLS_NSS
30     HASHContext* mpContext;
31 
32     HASH_HashType getNSSType() const
33     {
34         switch (meType)
35         {
36             case HashType::MD5:
37                 return HASH_AlgMD5;
38             case HashType::SHA1:
39                 return HASH_AlgSHA1;
40             case HashType::SHA256:
41                 return HASH_AlgSHA256;
42             case HashType::SHA512:
43                 return HASH_AlgSHA512;
44         }
45 
46         return HASH_AlgNULL;
47     }
48 #elif USE_TLS_OPENSSL
49     EVP_MD_CTX* mpContext;
50 
51     const EVP_MD* getOpenSSLType() const
52     {
53         switch (meType)
54         {
55             case HashType::MD5:
56                 return EVP_md5();
57             case HashType::SHA1:
58                 return EVP_sha1();
59             case HashType::SHA256:
60                 return EVP_sha256();
61             case HashType::SHA512:
62                 return EVP_sha512();
63         }
64 
65         return nullptr;
66     }
67 #endif
68 
69     HashType const meType;
70 
71     HashImpl(HashType eType):
72         meType(eType)
73     {
74 
75 #if USE_TLS_NSS
76         NSS_NoDB_Init(nullptr);
77         mpContext = HASH_Create(getNSSType());
78         HASH_Begin(mpContext);
79 #elif USE_TLS_OPENSSL
80         mpContext = EVP_MD_CTX_create();
81         EVP_DigestInit_ex(mpContext, getOpenSSLType(), NULL);
82 #endif
83     }
84 
85     ~HashImpl()
86     {
87 #if USE_TLS_NSS
88         HASH_Destroy(mpContext);
89 #elif USE_TLS_OPENSSL
90         EVP_MD_CTX_destroy(mpContext);
91 #endif
92     }
93 };
94 
95 Hash::Hash(HashType eType):
96     mpImpl(new HashImpl(eType))
97 {
98 }
99 
100 Hash::~Hash()
101 {
102 }
103 
104 void Hash::update(const unsigned char* pInput, size_t length)
105 {
106 #if USE_TLS_NSS
107     HASH_Update(mpImpl->mpContext, pInput, length);
108 #elif USE_TLS_OPENSSL
109     EVP_DigestUpdate(mpImpl->mpContext, pInput, length);
110 #else
111     (void)pInput;
112     (void)length;
113 #endif
114 }
115 
116 std::vector<unsigned char> Hash::finalize()
117 {
118     std::vector<unsigned char> hash(getLength(), 0);
119     unsigned int digestWrittenLength;
120 #if USE_TLS_NSS
121     HASH_End(mpImpl->mpContext, hash.data(), &digestWrittenLength, getLength());
122 #elif USE_TLS_OPENSSL
123     EVP_DigestFinal_ex(mpImpl->mpContext, hash.data(), &digestWrittenLength);
124 #else
125     (void)digestWrittenLength;
126 #endif
127 
128     return hash;
129 }
130 
131 size_t Hash::getLength() const
132 {
133     switch (mpImpl->meType)
134     {
135         case HashType::MD5:
136             return 16;
137         case HashType::SHA1:
138             return 20;
139         case HashType::SHA256:
140             return 32;
141         case HashType::SHA512:
142             return 64;
143     }
144 
145     return 0;
146 }
147 
148 std::vector<unsigned char> Hash::calculateHash(const unsigned char* pInput, size_t length, HashType eType)
149 {
150     Hash aHash(eType);
151     aHash.update(pInput, length);
152     return aHash.finalize();
153 }
154 
155 std::vector<unsigned char> Hash::calculateHash(
156         const unsigned char* pInput, size_t nLength,
157         const unsigned char* pSalt, size_t nSaltLen,
158         sal_uInt32 nSpinCount,
159         IterCount eIterCount,
160         HashType eType)
161 {
162     if (!pSalt)
163         nSaltLen = 0;
164 
165     if (!nSaltLen && !nSpinCount)
166         return calculateHash( pInput, nLength, eType);
167 
168     Hash aHash(eType);
169     if (nSaltLen)
170     {
171         std::vector<unsigned char> initialData( nSaltLen + nLength);
172         std::copy( pSalt, pSalt + nSaltLen, initialData.begin());
173         std::copy( pInput, pInput + nLength, initialData.begin() + nSaltLen);
174         aHash.update( initialData.data(), initialData.size());
175         rtl_secureZeroMemory( initialData.data(), initialData.size());
176     }
177     else
178     {
179         aHash.update( pInput, nLength);
180     }
181     std::vector<unsigned char> hash( aHash.finalize());
182 
183     if (nSpinCount)
184     {
185         // https://msdn.microsoft.com/en-us/library/dd920692
186         // says the iteration is concatenated after the hash.
187         // https://msdn.microsoft.com/en-us/library/dd924776 and
188         // https://msdn.microsoft.com/en-us/library/dd925430
189         // say the iteration is prepended to the hash.
190         const size_t nAddIter = (eIterCount == IterCount::NONE ? 0 : 4);
191         const size_t nIterPos = (eIterCount == IterCount::APPEND ? hash.size() : 0);
192         const size_t nHashPos = (eIterCount == IterCount::PREPEND ? nAddIter : 0);
193         std::vector<unsigned char> data( hash.size() + nAddIter, 0);
194         for (sal_uInt32 i = 0; i < nSpinCount; ++i)
195         {
196             std::copy( hash.begin(), hash.end(), data.begin() + nHashPos);
197             if (nAddIter)
198             {
199 #ifdef OSL_BIGENDIAN
200                 sal_uInt32 be = i;
201                 sal_uInt8* p = reinterpret_cast<sal_uInt8*>(&be);
202                 std::swap( p[0], p[3] );
203                 std::swap( p[1], p[2] );
204                 memcpy( data.data() + nIterPos, &be, nAddIter);
205 #else
206                 memcpy( data.data() + nIterPos, &i, nAddIter);
207 #endif
208             }
209             /* TODO: isn't there something better than
210              * creating/finalizing/destroying on each iteration? */
211             Hash aReHash(eType);
212             aReHash.update( data.data(), data.size());
213             hash = aReHash.finalize();
214         }
215     }
216 
217     return hash;
218 }
219 
220 std::vector<unsigned char> Hash::calculateHash(
221         const OUString& rPassword,
222         const std::vector<unsigned char>& rSaltValue,
223         sal_uInt32 nSpinCount,
224         IterCount eIterCount,
225         HashType eType)
226 {
227     const unsigned char* pPassBytes = reinterpret_cast<const unsigned char*>(rPassword.getStr());
228     const size_t nPassBytesLen = rPassword.getLength() * 2;
229 #ifdef OSL_BIGENDIAN
230     // Swap UTF16-BE to UTF16-LE
231     std::vector<unsigned char> vPass;
232     if (nPassBytesLen)
233     {
234         vPass.resize( nPassBytesLen);
235         std::copy( pPassBytes, pPassBytes + nPassBytesLen, vPass.begin());
236         unsigned char* p = vPass.data();
237         unsigned char const * const pEnd = p + nPassBytesLen;
238         for ( ; p < pEnd; p += 2 )
239         {
240             std::swap( p[0], p[1] );
241         }
242         pPassBytes = vPass.data();
243     }
244 #endif
245     return calculateHash( pPassBytes, nPassBytesLen, rSaltValue.data(), rSaltValue.size(), nSpinCount,
246             eIterCount, eType);
247 }
248 
249 }
250 
251 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
252