diff options
Diffstat (limited to 'comphelper/source/misc/docpasswordhelper.cxx')
-rw-r--r-- | comphelper/source/misc/docpasswordhelper.cxx | 233 |
1 files changed, 232 insertions, 1 deletions
diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx index ea25cb795a53..37941352ae28 100644 --- a/comphelper/source/misc/docpasswordhelper.cxx +++ b/comphelper/source/misc/docpasswordhelper.cxx @@ -1,4 +1,4 @@ -/************************************************************************* +/*********************************************************************** * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -32,7 +32,12 @@ #include <com/sun/star/task/XInteractionHandler.hpp> #include "comphelper/mediadescriptor.hxx" +#include <osl/time.h> +#include <rtl/digest.h> +#include <rtl/random.h> + using ::rtl::OUString; +using ::com::sun::star::uno::Sequence; using ::com::sun::star::uno::Exception; using ::com::sun::star::uno::Reference; using ::com::sun::star::uno::UNO_SET_THROW; @@ -42,15 +47,241 @@ using ::com::sun::star::task::PasswordRequestMode_PASSWORD_REENTER; using ::com::sun::star::task::XInteractionHandler; using ::com::sun::star::task::XInteractionRequest; +using namespace ::com::sun::star; + namespace comphelper { // ============================================================================ +static uno::Sequence< sal_Int8 > GeneratePBKDF2Hash( const ::rtl::OUString& aPassword, const uno::Sequence< sal_Int8 >& aSalt, sal_Int32 nCount, sal_Int32 nHashLength ) +{ + uno::Sequence< sal_Int8 > aResult; + + if ( aPassword.getLength() && aSalt.getLength() && nCount && nHashLength ) + { + ::rtl::OString aBytePass = ::rtl::OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ); + aResult.realloc( 16 ); + rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8 * > ( aResult.getArray() ), + aResult.getLength(), + reinterpret_cast < const sal_uInt8 * > ( aBytePass.getStr() ), + aBytePass.getLength(), + reinterpret_cast < const sal_uInt8 * > ( aSalt.getConstArray() ), + aSalt.getLength(), + nCount ); + } + + return aResult; +} + +// ============================================================================ + IDocPasswordVerifier::~IDocPasswordVerifier() { } // ============================================================================ +uno::Sequence< beans::PropertyValue > DocPasswordHelper::GenerateNewModifyPasswordInfo( const ::rtl::OUString& aPassword ) +{ + uno::Sequence< beans::PropertyValue > aResult; + + uno::Sequence< sal_Int8 > aSalt( 16 ); + sal_Int32 nCount = 1024; + + TimeValue aTime; + osl_getSystemTime( &aTime ); + rtlRandomPool aRandomPool = rtl_random_createPool (); + rtl_random_addBytes ( aRandomPool, &aTime, 8 ); + + rtl_random_getBytes ( aRandomPool, aSalt.getArray(), 16 ); + + uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash( aPassword, aSalt, nCount, 16 ); + if ( aNewHash.getLength() ) + { + aResult.realloc( 4 ); + aResult[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "algorithm-name" ) ); + aResult[0].Value <<= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PBKDF2" ) ); + aResult[1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "salt" ) ); + aResult[1].Value <<= aSalt; + aResult[2].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "iteration-count" ) ); + aResult[2].Value <<= nCount; + aResult[3].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hash" ) ); + aResult[3].Value <<= aNewHash; + } + + // Clean up random pool memory + rtl_random_destroyPool ( aRandomPool ); + + return aResult; +} + +// ============================================================================ +sal_Bool DocPasswordHelper::IsModifyPasswordCorrect( const ::rtl::OUString& aPassword, const uno::Sequence< beans::PropertyValue >& aInfo ) +{ + sal_Bool bResult = sal_False; + if ( aPassword.getLength() && aInfo.getLength() ) + { + ::rtl::OUString sAlgorithm; + uno::Sequence< sal_Int8 > aSalt; + uno::Sequence< sal_Int8 > aHash; + sal_Int32 nCount = 0; + + for ( sal_Int32 nInd = 0; nInd < aInfo.getLength(); nInd++ ) + { + if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "algorithm-name" ) ) ) ) + aInfo[nInd].Value >>= sAlgorithm; + else if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "salt" ) ) ) ) + aInfo[nInd].Value >>= aSalt; + else if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "iteration-count" ) ) ) ) + aInfo[nInd].Value >>= nCount; + else if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hash" ) ) ) ) + aInfo[nInd].Value >>= aHash; + } + + if ( sAlgorithm.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PBKDF2" ) ) ) + && aSalt.getLength() && nCount > 0 && aHash.getLength() ) + { + uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash( aPassword, aSalt, nCount, aHash.getLength() ); + for ( sal_Int32 nInd = 0; nInd < aNewHash.getLength() && nInd < aHash.getLength() && aNewHash[nInd] == aHash[nInd]; nInd ++ ) + { + if ( nInd == aNewHash.getLength() - 1 && nInd == aHash.getLength() - 1 ) + bResult = sal_True; + } + } + } + + return bResult; +} + +// ============================================================================ +sal_uInt32 DocPasswordHelper::GetWordHashAsUINT32( + const ::rtl::OUString& aUString ) +{ + static sal_uInt16 pInitialCode[] = { + 0xE1F0, // 1 + 0x1D0F, // 2 + 0xCC9C, // 3 + 0x84C0, // 4 + 0x110C, // 5 + 0x0E10, // 6 + 0xF1CE, // 7 + 0x313E, // 8 + 0x1872, // 9 + 0xE139, // 10 + 0xD40F, // 11 + 0x84F9, // 12 + 0x280C, // 13 + 0xA96A, // 14 + 0x4EC3 // 15 + }; + + static sal_uInt16 pEncryptionMatrix[15][7] = { + { 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09}, // last-14 + { 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF}, // last-13 + { 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0}, // last-12 + { 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40}, // last-11 + { 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5}, // last-10 + { 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A}, // last-9 + { 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9}, // last-8 + { 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0}, // last-7 + { 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC}, // last-6 + { 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10}, // last-5 + { 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168}, // last-4 + { 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C}, // last-3 + { 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD}, // last-2 + { 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC}, // last-1 + { 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4} // last + }; + + sal_uInt32 nResult = 0; + sal_uInt32 nLen = aUString.getLength(); + + if ( nLen ) + { + if ( nLen > 15 ) + nLen = 15; + + sal_uInt16 nHighResult = pInitialCode[nLen - 1]; + sal_uInt16 nLowResult = 0; + + const sal_Unicode* pStr = aUString.getStr(); + for ( sal_uInt32 nInd = 0; nInd < nLen; nInd++ ) + { + // NO Encoding during conversion! + // The specification says that the low byte should be used in case it is not NULL + char nHighChar = (char)( pStr[nInd] >> 8 ); + char nLowChar = (char)( pStr[nInd] & 0xFF ); + char nChar = nLowChar ? nLowChar : nHighChar; + + for ( int nMatrixInd = 0; nMatrixInd < 7; ++nMatrixInd ) + { + if ( ( nChar & ( 1 << nMatrixInd ) ) != 0 ) + nHighResult = nHighResult ^ pEncryptionMatrix[15 - nLen + nInd][nMatrixInd]; + } + + nLowResult = ( ( ( nLowResult >> 14 ) & 0x0001 ) | ( ( nLowResult << 1 ) & 0x7FFF ) ) ^ nChar; + } + + nLowResult = (sal_uInt16)( ( ( ( nLowResult >> 14 ) & 0x001 ) | ( ( nLowResult << 1 ) & 0x7FF ) ) ^ nLen ^ 0xCE4B ); + + nResult = ( nHighResult << 16 ) | nLowResult; + } + + return nResult; +} + +// ============================================================================ +Sequence< sal_Int8 > DocPasswordHelper::GetWordHashAsSequence( + const ::rtl::OUString& aUString ) +{ + sal_uInt32 nHash = GetWordHashAsUINT32( aUString ); + Sequence< sal_Int8 > aResult( 4 ); + aResult[0] = ( nHash >> 24 ); + aResult[1] = ( ( nHash >> 16 ) & 0xFF ); + aResult[2] = ( ( nHash >> 8 ) & 0xFF ); + aResult[3] = ( nHash & 0xFF ); + + return aResult; +} + +// ============================================================================ +sal_uInt16 DocPasswordHelper::GetXLHashAsUINT16( + const ::rtl::OUString& aUString, + rtl_TextEncoding nEnc ) +{ + sal_uInt16 nResult = 0; + + ::rtl::OString aString = ::rtl::OUStringToOString( aUString, nEnc ); + + if ( aString.getLength() && aString.getLength() <= SAL_MAX_UINT16 ) + { + for ( sal_Int32 nInd = aString.getLength() - 1; nInd >= 0; nInd-- ) + { + nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF ); + nResult ^= aString.getStr()[nInd]; + } + + nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF ); + nResult ^= ( 0x8000 | ( 'N' << 8 ) | 'K' ); + nResult ^= aString.getLength(); + } + + return nResult; +} + +// ============================================================================ +Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( + const ::rtl::OUString& aUString, + rtl_TextEncoding nEnc ) +{ + sal_uInt16 nHash = GetXLHashAsUINT16( aUString, nEnc ); + Sequence< sal_Int8 > aResult( 2 ); + aResult[0] = ( nHash >> 8 ); + aResult[1] = ( nHash & 0xFF ); + + return aResult; +} + +// ============================================================================ /*static*/ OUString DocPasswordHelper::requestAndVerifyDocPassword( IDocPasswordVerifier& rVerifier, |