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
