2017-04-21 05:38:21 +02:00
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2.0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*/
2020-03-26 09:43:59 +01:00
# include <sal/config.h>
2020-10-31 14:32:20 +01:00
# include <com/sun/star/uno/RuntimeException.hpp>
2017-04-21 05:38:21 +02:00
# include <comphelper/hash.hxx>
2018-02-23 18:23:04 +01:00
# include <rtl/ustring.hxx>
# include <rtl/alloc.h>
# include <osl/endian.h>
2017-04-21 05:38:21 +02:00
# include <config_oox.h>
# if USE_TLS_NSS
# include <nss.h>
2022-11-04 17:44:10 +01:00
# include <nspr.h>
2017-04-21 05:38:21 +02:00
# include <sechash.h>
# elif USE_TLS_OPENSSL
# include <openssl/evp.h>
# include <openssl/sha.h>
# endif // USE_TLS_OPENSSL
namespace comphelper {
struct HashImpl
{
# if USE_TLS_NSS
HASHContext * mpContext ;
HASH_HashType getNSSType ( ) const
{
switch ( meType )
{
2017-04-21 05:51:26 +02:00
case HashType : : MD5 :
return HASH_AlgMD5 ;
2017-04-21 05:38:21 +02:00
case HashType : : SHA1 :
return HASH_AlgSHA1 ;
case HashType : : SHA256 :
return HASH_AlgSHA256 ;
2023-08-22 22:10:20 +02:00
case HashType : : SHA384 :
return HASH_AlgSHA384 ;
2017-04-21 05:38:21 +02:00
case HashType : : SHA512 :
return HASH_AlgSHA512 ;
}
return HASH_AlgNULL ;
}
# elif USE_TLS_OPENSSL
EVP_MD_CTX * mpContext ;
2017-04-22 21:07:32 +01:00
const EVP_MD * getOpenSSLType ( ) const
2017-04-21 05:38:21 +02:00
{
switch ( meType )
{
2017-04-21 05:51:26 +02:00
case HashType : : MD5 :
return EVP_md5 ( ) ;
2017-04-21 05:38:21 +02:00
case HashType : : SHA1 :
return EVP_sha1 ( ) ;
case HashType : : SHA256 :
return EVP_sha256 ( ) ;
2023-08-22 22:10:20 +02:00
case HashType : : SHA384 :
return EVP_sha384 ( ) ;
2017-04-21 05:38:21 +02:00
case HashType : : SHA512 :
return EVP_sha512 ( ) ;
}
return nullptr ;
}
# endif
2018-07-19 16:28:37 +02:00
HashType const meType ;
2017-04-21 05:38:21 +02:00
HashImpl ( HashType eType ) :
meType ( eType )
{
# if USE_TLS_NSS
2022-11-07 14:29:09 +01:00
if ( ! NSS_IsInitialized ( ) )
{
auto const e = NSS_NoDB_Init ( nullptr ) ;
if ( e ! = SECSuccess )
{
PRErrorCode error = PR_GetError ( ) ;
const char * errorText = PR_ErrorToName ( error ) ;
throw css : : uno : : RuntimeException ( " NSS_NoDB_Init failed with " + OUString ( errorText , strlen ( errorText ) , RTL_TEXTENCODING_UTF8 ) + " ( " + OUString : : number ( static_cast < int > ( error ) ) + " ) " ) ;
}
2020-03-26 09:43:59 +01:00
}
2017-04-21 05:38:21 +02:00
mpContext = HASH_Create ( getNSSType ( ) ) ;
HASH_Begin ( mpContext ) ;
# elif USE_TLS_OPENSSL
mpContext = EVP_MD_CTX_create ( ) ;
2020-08-24 16:47:25 +01:00
EVP_DigestInit_ex ( mpContext , getOpenSSLType ( ) , nullptr ) ;
2017-04-21 05:38:21 +02:00
# endif
}
~ HashImpl ( )
{
# if USE_TLS_NSS
HASH_Destroy ( mpContext ) ;
# elif USE_TLS_OPENSSL
EVP_MD_CTX_destroy ( mpContext ) ;
# endif
}
} ;
Hash : : Hash ( HashType eType ) :
mpImpl ( new HashImpl ( eType ) )
{
}
Hash : : ~ Hash ( )
{
}
void Hash : : update ( const unsigned char * pInput , size_t length )
{
# if USE_TLS_NSS
HASH_Update ( mpImpl - > mpContext , pInput , length ) ;
# elif USE_TLS_OPENSSL
EVP_DigestUpdate ( mpImpl - > mpContext , pInput , length ) ;
2017-06-09 12:59:19 +02:00
# else
( void ) pInput ;
( void ) length ;
2017-04-21 05:38:21 +02:00
# endif
}
std : : vector < unsigned char > Hash : : finalize ( )
{
std : : vector < unsigned char > hash ( getLength ( ) , 0 ) ;
unsigned int digestWrittenLength ;
# if USE_TLS_NSS
HASH_End ( mpImpl - > mpContext , hash . data ( ) , & digestWrittenLength , getLength ( ) ) ;
# elif USE_TLS_OPENSSL
EVP_DigestFinal_ex ( mpImpl - > mpContext , hash . data ( ) , & digestWrittenLength ) ;
2017-06-09 12:59:19 +02:00
# else
( void ) digestWrittenLength ;
2017-04-21 05:38:21 +02:00
# endif
return hash ;
}
size_t Hash : : getLength ( ) const
{
switch ( mpImpl - > meType )
{
2017-04-21 05:51:26 +02:00
case HashType : : MD5 :
2020-07-14 21:16:07 +02:00
return MD5_HASH_LENGTH ;
2017-04-21 05:38:21 +02:00
case HashType : : SHA1 :
2020-07-14 21:16:07 +02:00
return SHA1_HASH_LENGTH ;
2017-04-21 05:38:21 +02:00
case HashType : : SHA256 :
2020-07-14 21:16:07 +02:00
return SHA256_HASH_LENGTH ;
2023-08-22 22:10:20 +02:00
case HashType : : SHA384 :
return SHA384_HASH_LENGTH ;
2017-04-21 05:38:21 +02:00
case HashType : : SHA512 :
2020-07-14 21:16:07 +02:00
return SHA512_HASH_LENGTH ;
2017-04-21 05:38:21 +02:00
}
return 0 ;
}
std : : vector < unsigned char > Hash : : calculateHash ( const unsigned char * pInput , size_t length , HashType eType )
{
Hash aHash ( eType ) ;
aHash . update ( pInput , length ) ;
return aHash . finalize ( ) ;
}
2018-02-23 18:23:04 +01:00
std : : vector < unsigned char > Hash : : calculateHash (
const unsigned char * pInput , size_t nLength ,
const unsigned char * pSalt , size_t nSaltLen ,
sal_uInt32 nSpinCount ,
2018-02-26 19:58:12 +01:00
IterCount eIterCount ,
2018-02-23 18:23:04 +01:00
HashType eType )
{
if ( ! pSalt )
nSaltLen = 0 ;
if ( ! nSaltLen & & ! nSpinCount )
return calculateHash ( pInput , nLength , eType ) ;
Hash aHash ( eType ) ;
if ( nSaltLen )
{
std : : vector < unsigned char > initialData ( nSaltLen + nLength ) ;
std : : copy ( pSalt , pSalt + nSaltLen , initialData . begin ( ) ) ;
std : : copy ( pInput , pInput + nLength , initialData . begin ( ) + nSaltLen ) ;
aHash . update ( initialData . data ( ) , initialData . size ( ) ) ;
rtl_secureZeroMemory ( initialData . data ( ) , initialData . size ( ) ) ;
}
else
{
aHash . update ( pInput , nLength ) ;
}
2018-02-24 11:45:34 +01:00
std : : vector < unsigned char > hash ( aHash . finalize ( ) ) ;
2018-02-23 18:23:04 +01:00
if ( nSpinCount )
{
// https://msdn.microsoft.com/en-us/library/dd920692
// says the iteration is concatenated after the hash.
2018-02-26 13:18:22 +01:00
// https://msdn.microsoft.com/en-us/library/dd924776 and
// https://msdn.microsoft.com/en-us/library/dd925430
// say the iteration is prepended to the hash.
2018-02-26 19:58:12 +01:00
const size_t nAddIter = ( eIterCount = = IterCount : : NONE ? 0 : 4 ) ;
const size_t nIterPos = ( eIterCount = = IterCount : : APPEND ? hash . size ( ) : 0 ) ;
const size_t nHashPos = ( eIterCount = = IterCount : : PREPEND ? nAddIter : 0 ) ;
std : : vector < unsigned char > data ( hash . size ( ) + nAddIter , 0 ) ;
2018-02-23 18:23:04 +01:00
for ( sal_uInt32 i = 0 ; i < nSpinCount ; + + i )
{
std : : copy ( hash . begin ( ) , hash . end ( ) , data . begin ( ) + nHashPos ) ;
2018-02-26 19:58:12 +01:00
if ( nAddIter )
{
2018-02-23 18:23:04 +01:00
# ifdef OSL_BIGENDIAN
2018-02-26 19:58:12 +01:00
sal_uInt32 be = i ;
sal_uInt8 * p = reinterpret_cast < sal_uInt8 * > ( & be ) ;
std : : swap ( p [ 0 ] , p [ 3 ] ) ;
std : : swap ( p [ 1 ] , p [ 2 ] ) ;
memcpy ( data . data ( ) + nIterPos , & be , nAddIter ) ;
2018-02-23 18:23:04 +01:00
# else
2018-02-26 19:58:12 +01:00
memcpy ( data . data ( ) + nIterPos , & i , nAddIter ) ;
2018-02-23 18:23:04 +01:00
# endif
2018-02-26 19:58:12 +01:00
}
2018-02-23 18:23:04 +01:00
/* TODO: isn't there something better than
* creating / finalizing / destroying on each iteration ? */
Hash aReHash ( eType ) ;
aReHash . update ( data . data ( ) , data . size ( ) ) ;
hash = aReHash . finalize ( ) ;
}
}
return hash ;
}
std : : vector < unsigned char > Hash : : calculateHash (
const OUString & rPassword ,
const std : : vector < unsigned char > & rSaltValue ,
sal_uInt32 nSpinCount ,
2018-02-26 19:58:12 +01:00
IterCount eIterCount ,
2018-02-23 18:23:04 +01:00
HashType eType )
{
const unsigned char * pPassBytes = reinterpret_cast < const unsigned char * > ( rPassword . getStr ( ) ) ;
const size_t nPassBytesLen = rPassword . getLength ( ) * 2 ;
2018-02-26 15:33:57 +01:00
# ifdef OSL_BIGENDIAN
// Swap UTF16-BE to UTF16-LE
std : : vector < unsigned char > vPass ;
if ( nPassBytesLen )
{
vPass . resize ( nPassBytesLen ) ;
std : : copy ( pPassBytes , pPassBytes + nPassBytesLen , vPass . begin ( ) ) ;
2018-02-26 16:44:38 +01:00
unsigned char * p = vPass . data ( ) ;
unsigned char const * const pEnd = p + nPassBytesLen ;
2018-02-26 15:33:57 +01:00
for ( ; p < pEnd ; p + = 2 )
{
std : : swap ( p [ 0 ] , p [ 1 ] ) ;
}
pPassBytes = vPass . data ( ) ;
}
# endif
2018-02-26 13:18:22 +01:00
return calculateHash ( pPassBytes , nPassBytesLen , rSaltValue . data ( ) , rSaltValue . size ( ) , nSpinCount ,
2018-02-26 19:58:12 +01:00
eIterCount , eType ) ;
2018-02-23 18:23:04 +01:00
}
2017-04-21 05:38:21 +02:00
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */