diff options
Diffstat (limited to 'filter/source/msfilter/mscodec.cxx')
-rw-r--r-- | filter/source/msfilter/mscodec.cxx | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/filter/source/msfilter/mscodec.cxx b/filter/source/msfilter/mscodec.cxx new file mode 100644 index 000000000000..f530dde3f1a3 --- /dev/null +++ b/filter/source/msfilter/mscodec.cxx @@ -0,0 +1,576 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_filter.hxx" +#include "filter/msfilter/mscodec.hxx" + +#include <osl/diagnose.h> +#include <algorithm> +#include <string.h> +#include <tools/solar.h> + +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/docpasswordhelper.hxx> + +#define DEBUG_MSO_ENCRYPTION_STD97 0 + +#if DEBUG_MSO_ENCRYPTION_STD97 +#include <stdio.h> +#endif + +using namespace ::com::sun::star; + +namespace msfilter { + +// ============================================================================ + +namespace { + +/** Rotates rnValue left by nBits bits. */ +template< typename Type > +inline void lclRotateLeft( Type& rnValue, int nBits ) +{ + OSL_ASSERT( + nBits >= 0 && + sal::static_int_cast< unsigned int >(nBits) < sizeof( Type ) * 8 ); + rnValue = static_cast< Type >( (rnValue << nBits) | (rnValue >> (sizeof( Type ) * 8 - nBits)) ); +} + +/** Rotates the lower nWidth bits of rnValue left by nBits bits. */ +template< typename Type > +inline void lclRotateLeft( Type& rnValue, sal_uInt8 nBits, sal_uInt8 nWidth ) +{ + OSL_ASSERT( (nBits < nWidth) && (nWidth < sizeof( Type ) * 8) ); + Type nMask = static_cast< Type >( (1UL << nWidth) - 1 ); + rnValue = static_cast< Type >( + ((rnValue << nBits) | ((rnValue & nMask) >> (nWidth - nBits))) & nMask ); +} + +sal_Size lclGetLen( const sal_uInt8* pnPassData, sal_Size nBufferSize ) +{ + sal_Size nLen = 0; + while( (nLen < nBufferSize) && pnPassData[ nLen ] ) ++nLen; + return nLen; +} + +sal_uInt16 lclGetKey( const sal_uInt8* pnPassData, sal_Size nBufferSize ) +{ + sal_Size nLen = lclGetLen( pnPassData, nBufferSize ); + if( !nLen ) return 0; + + sal_uInt16 nKey = 0; + sal_uInt16 nKeyBase = 0x8000; + sal_uInt16 nKeyEnd = 0xFFFF; + const sal_uInt8* pnChar = pnPassData + nLen - 1; + for( sal_Size nIndex = 0; nIndex < nLen; ++nIndex, --pnChar ) + { + sal_uInt8 cChar = *pnChar & 0x7F; + for( sal_uInt8 nBit = 0; nBit < 8; ++nBit ) + { + lclRotateLeft( nKeyBase, 1 ); + if( nKeyBase & 1 ) nKeyBase ^= 0x1020; + if( cChar & 1 ) nKey ^= nKeyBase; + cChar >>= 1; + lclRotateLeft( nKeyEnd, 1 ); + if( nKeyEnd & 1 ) nKeyEnd ^= 0x1020; + } + } + return nKey ^ nKeyEnd; +} + +sal_uInt16 lclGetHash( const sal_uInt8* pnPassData, sal_Size nBufferSize ) +{ + sal_Size nLen = lclGetLen( pnPassData, nBufferSize ); + + sal_uInt16 nHash = static_cast< sal_uInt16 >( nLen ); + if( nLen ) + nHash ^= 0xCE4B; + + const sal_uInt8* pnChar = pnPassData; + for( sal_Size nIndex = 0; nIndex < nLen; ++nIndex, ++pnChar ) + { + sal_uInt16 cChar = *pnChar; + sal_uInt8 nRot = static_cast< sal_uInt8 >( (nIndex + 1) % 15 ); + lclRotateLeft( cChar, nRot, 15 ); + nHash ^= cChar; + } + return nHash; +} + + +} // namespace + +// ============================================================================ + +MSCodec_Xor95::MSCodec_Xor95(int nRotateDistance) : + mnOffset( 0 ), + mnKey( 0 ), + mnHash( 0 ), + mnRotateDistance( nRotateDistance ) +{ + (void)memset( mpnKey, 0, sizeof( mpnKey ) ); +} + +MSCodec_Xor95::~MSCodec_Xor95() +{ + (void)memset( mpnKey, 0, sizeof( mpnKey ) ); + mnKey = mnHash = 0; +} + +void MSCodec_Xor95::InitKey( const sal_uInt8 pnPassData[ 16 ] ) +{ + mnKey = lclGetKey( pnPassData, 16 ); + mnHash = lclGetHash( pnPassData, 16 ); + + (void)memcpy( mpnKey, pnPassData, 16 ); + + static const sal_uInt8 spnFillChars[] = + { + 0xBB, 0xFF, 0xFF, 0xBA, + 0xFF, 0xFF, 0xB9, 0x80, + 0x00, 0xBE, 0x0F, 0x00, + 0xBF, 0x0F, 0x00 + }; + + sal_Size nIndex; + sal_Size nLen = lclGetLen( pnPassData, 16 ); + const sal_uInt8* pnFillChar = spnFillChars; + for( nIndex = nLen; nIndex < sizeof( mpnKey ); ++nIndex, ++pnFillChar ) + mpnKey[ nIndex ] = *pnFillChar; + + SVBT16 pnOrigKey; + ShortToSVBT16( mnKey, pnOrigKey ); + sal_uInt8* pnKeyChar = mpnKey; + for( nIndex = 0; nIndex < sizeof( mpnKey ); ++nIndex, ++pnKeyChar ) + { + *pnKeyChar ^= pnOrigKey[ nIndex & 0x01 ]; + lclRotateLeft( *pnKeyChar, mnRotateDistance ); + } +} + +sal_Bool MSCodec_Xor95::InitCodec( const uno::Sequence< beans::NamedValue >& aData ) +{ + sal_Bool bResult = sal_False; + + ::comphelper::SequenceAsHashMap aHashData( aData ); + uno::Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95EncryptionKey" ) ), uno::Sequence< sal_Int8 >() ); + + if ( aKey.getLength() == 16 ) + { + (void)memcpy( mpnKey, aKey.getConstArray(), 16 ); + bResult = sal_True; + + mnKey = (sal_uInt16)aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95BaseKey" ) ), (sal_Int16)0 ); + mnHash = (sal_uInt16)aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95PasswordHash" ) ), (sal_Int16)0 ); + } + else + OSL_FAIL( "Unexpected key size!\n" ); + + return bResult; +} + +uno::Sequence< beans::NamedValue > MSCodec_Xor95::GetEncryptionData() +{ + ::comphelper::SequenceAsHashMap aHashData; + aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95EncryptionKey" ) ) ] <<= uno::Sequence<sal_Int8>( (sal_Int8*)mpnKey, 16 ); + aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95BaseKey" ) ) ] <<= (sal_Int16)mnKey; + aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "XOR95PasswordHash" ) ) ] <<= (sal_Int16)mnHash; + + return aHashData.getAsConstNamedValueList(); +} + +bool MSCodec_Xor95::VerifyKey( sal_uInt16 nKey, sal_uInt16 nHash ) const +{ + return (nKey == mnKey) && (nHash == mnHash); +} + +void MSCodec_Xor95::InitCipher() +{ + mnOffset = 0; +} + +void MSCodec_XorXLS95::Decode( sal_uInt8* pnData, sal_Size nBytes ) +{ + const sal_uInt8* pnCurrKey = mpnKey + mnOffset; + const sal_uInt8* pnKeyLast = mpnKey + 0x0F; + + for( const sal_uInt8* pnDataEnd = pnData + nBytes; pnData < pnDataEnd; ++pnData ) + { + lclRotateLeft( *pnData, 3 ); + *pnData ^= *pnCurrKey; + if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = mpnKey; + } + + // update mnOffset + Skip( nBytes ); +} + +void MSCodec_XorWord95::Decode( sal_uInt8* pnData, sal_Size nBytes ) +{ + const sal_uInt8* pnCurrKey = mpnKey + mnOffset; + const sal_uInt8* pnKeyLast = mpnKey + 0x0F; + + for( const sal_uInt8* pnDataEnd = pnData + nBytes; pnData < pnDataEnd; ++pnData ) + { + const sal_uInt8 cChar = *pnData ^ *pnCurrKey; + if (*pnData && cChar) + *pnData = cChar; + if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = mpnKey; + } + + // update mnOffset + Skip( nBytes ); +} + + +void MSCodec_Xor95::Skip( sal_Size nBytes ) +{ + mnOffset = (mnOffset + nBytes) & 0x0F; +} + +// ============================================================================ + +MSCodec_Std97::MSCodec_Std97 () +{ + m_hCipher = rtl_cipher_create ( + rtl_Cipher_AlgorithmARCFOUR, rtl_Cipher_ModeStream); + OSL_ASSERT(m_hCipher != 0); + + m_hDigest = rtl_digest_create ( + rtl_Digest_AlgorithmMD5); + OSL_ASSERT(m_hDigest != 0); + + (void)memset (m_pDigestValue, 0, sizeof(m_pDigestValue)); + (void)memset (m_pDocId, 0, sizeof(m_pDocId)); +} + +MSCodec_Std97::~MSCodec_Std97 () +{ + (void)memset (m_pDigestValue, 0, sizeof(m_pDigestValue)); + (void)memset (m_pDocId, 0, sizeof(m_pDocId)); + rtl_digest_destroy (m_hDigest); + rtl_cipher_destroy (m_hCipher); +} + +#if 0 +#if DEBUG_MSO_ENCRYPTION_STD97 +static void lcl_PrintKeyData(const sal_uInt8* pKeyData, const char* msg) +{ + printf("pKeyData: (%s)\n", msg); + for (int j = 0; j < 4; ++j) + { + for (int i = 0; i < 16; ++i) + printf("%2.2x ", pKeyData[j*16+i]); + printf("\n"); + } +} +#else +static void lcl_PrintKeyData(const sal_uInt8* /*pKeyData*/, const char* /*msg*/) +{ +} +#endif +#endif + +#if DEBUG_MSO_ENCRYPTION_STD97 +static void lcl_PrintDigest(const sal_uInt8* pDigest, const char* msg) +{ + printf("digest: (%s)\n", msg); + for (int i = 0; i < 16; ++i) + printf("%2.2x ", pDigest[i]); + printf("\n"); +} +#else +static void lcl_PrintDigest(const sal_uInt8* /*pDigest*/, const char* /*msg*/) +{ +} +#endif + +sal_Bool MSCodec_Std97::InitCodec( const uno::Sequence< beans::NamedValue >& aData ) +{ +#if DEBUG_MSO_ENCRYPTION_STD97 + fprintf(stdout, "MSCodec_Std97::InitCodec: --begin\n");fflush(stdout); +#endif + sal_Bool bResult = sal_False; + + ::comphelper::SequenceAsHashMap aHashData( aData ); + uno::Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97EncryptionKey" ) ), uno::Sequence< sal_Int8 >() ); + + if ( aKey.getLength() == RTL_DIGEST_LENGTH_MD5 ) + { + (void)memcpy( m_pDigestValue, aKey.getConstArray(), RTL_DIGEST_LENGTH_MD5 ); + uno::Sequence< sal_Int8 > aUniqueID = aHashData.getUnpackedValueOrDefault( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97UniqueID" ) ), uno::Sequence< sal_Int8 >() ); + if ( aUniqueID.getLength() == 16 ) + { + (void)memcpy( m_pDocId, aUniqueID.getConstArray(), 16 ); + bResult = sal_True; + lcl_PrintDigest(m_pDigestValue, "digest value"); + lcl_PrintDigest(m_pDocId, "DocId value"); + } + else + OSL_FAIL( "Unexpected document ID!\n" ); + } + else + OSL_FAIL( "Unexpected key size!\n" ); + + return bResult; +} + +uno::Sequence< beans::NamedValue > MSCodec_Std97::GetEncryptionData() +{ + ::comphelper::SequenceAsHashMap aHashData; + aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97EncryptionKey" ) ) ] <<= uno::Sequence< sal_Int8 >( (sal_Int8*)m_pDigestValue, RTL_DIGEST_LENGTH_MD5 ); + aHashData[ ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "STD97UniqueID" ) ) ] <<= uno::Sequence< sal_Int8 >( (sal_Int8*)m_pDocId, 16 ); + + return aHashData.getAsConstNamedValueList(); +} + +void MSCodec_Std97::InitKey ( + const sal_uInt16 pPassData[16], + const sal_uInt8 pDocId[16]) +{ +#if DEBUG_MSO_ENCRYPTION_STD97 + fprintf(stdout, "MSCodec_Std97::InitKey: --begin\n");fflush(stdout); +#endif + uno::Sequence< sal_Int8 > aKey = ::comphelper::DocPasswordHelper::GenerateStd97Key( pPassData, uno::Sequence< sal_Int8 >( (sal_Int8*)pDocId, 16 ) ); + // Fill raw digest of above updates into DigestValue. + + if ( aKey.getLength() == sizeof(m_pDigestValue) ) + (void)memcpy ( m_pDigestValue, aKey.getConstArray(), sizeof(m_pDigestValue) ); + else + memset( m_pDigestValue, 0, sizeof(m_pDigestValue) ); + + lcl_PrintDigest(m_pDigestValue, "digest value"); + + (void)memcpy (m_pDocId, pDocId, 16); + + lcl_PrintDigest(m_pDocId, "DocId value"); +} + +bool MSCodec_Std97::VerifyKey ( + const sal_uInt8 pSaltData[16], + const sal_uInt8 pSaltDigest[16]) +{ + // both the salt data and salt digest (hash) come from the document being imported. + +#if DEBUG_MSO_ENCRYPTION_STD97 + fprintf(stdout, "MSCodec_Std97::VerifyKey: \n"); + lcl_PrintDigest(pSaltData, "salt data"); + lcl_PrintDigest(pSaltDigest, "salt hash"); +#endif + bool result = false; + + if (InitCipher(0)) + { + sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5]; + GetDigestFromSalt(pSaltData, pDigest); + + sal_uInt8 pBuffer[16]; + // Decode original SaltDigest into Buffer. + rtl_cipher_decode ( + m_hCipher, pSaltDigest, 16, pBuffer, sizeof(pBuffer)); + + // Compare Buffer with computed Digest. + result = (memcmp (pBuffer, pDigest, sizeof(pDigest)) == 0); + + // Erase Buffer and Digest arrays. + (void)memset (pBuffer, 0, sizeof(pBuffer)); + (void)memset (pDigest, 0, sizeof(pDigest)); + } + + return (result); +} + +bool MSCodec_Std97::InitCipher (sal_uInt32 nCounter) +{ + rtlCipherError result; + sal_uInt8 pKeyData[64]; // 512-bit message block + + // Initialize KeyData array. + (void)memset (pKeyData, 0, sizeof(pKeyData)); + + // Fill 40 bit of DigestValue into [0..4]. + (void)memcpy (pKeyData, m_pDigestValue, 5); + + // Fill counter into [5..8]. + pKeyData[ 5] = sal_uInt8((nCounter >> 0) & 0xff); + pKeyData[ 6] = sal_uInt8((nCounter >> 8) & 0xff); + pKeyData[ 7] = sal_uInt8((nCounter >> 16) & 0xff); + pKeyData[ 8] = sal_uInt8((nCounter >> 24) & 0xff); + + pKeyData[ 9] = 0x80; + pKeyData[56] = 0x48; + + // Fill raw digest of KeyData into KeyData. + (void)rtl_digest_updateMD5 ( + m_hDigest, pKeyData, sizeof(pKeyData)); + (void)rtl_digest_rawMD5 ( + m_hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5); + + // Initialize Cipher with KeyData (for decoding). + result = rtl_cipher_init ( + m_hCipher, rtl_Cipher_DirectionBoth, + pKeyData, RTL_DIGEST_LENGTH_MD5, 0, 0); + + // Erase KeyData array and leave. + (void)memset (pKeyData, 0, sizeof(pKeyData)); + + return (result == rtl_Cipher_E_None); +} + +bool MSCodec_Std97::CreateSaltDigest( const sal_uInt8 nSaltData[16], sal_uInt8 nSaltDigest[16] ) +{ +#if DEBUG_MSO_ENCRYPTION_STD97 + lcl_PrintDigest(nSaltData, "salt data"); +#endif + bool result = false; + + if (InitCipher(0)) + { + sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5]; + GetDigestFromSalt(nSaltData, pDigest); + + rtl_cipher_decode ( + m_hCipher, pDigest, 16, pDigest, sizeof(pDigest)); + + (void)memcpy(nSaltDigest, pDigest, 16); + } + + return (result); +} + +bool MSCodec_Std97::Encode ( + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + rtlCipherError result; + + result = rtl_cipher_encode ( + m_hCipher, pData, nDatLen, pBuffer, nBufLen); + + return (result == rtl_Cipher_E_None); +} + +bool MSCodec_Std97::Decode ( + const void *pData, sal_Size nDatLen, + sal_uInt8 *pBuffer, sal_Size nBufLen) +{ + rtlCipherError result; + + result = rtl_cipher_decode ( + m_hCipher, pData, nDatLen, pBuffer, nBufLen); + + return (result == rtl_Cipher_E_None); +} + +bool MSCodec_Std97::Skip( sal_Size nDatLen ) +{ + sal_uInt8 pnDummy[ 1024 ]; + sal_Size nDatLeft = nDatLen; + bool bResult = true; + + while (bResult && nDatLeft) + { + sal_Size nBlockLen = ::std::min< sal_Size >( nDatLeft, sizeof(pnDummy) ); + bResult = Decode( pnDummy, nBlockLen, pnDummy, nBlockLen ); + nDatLeft -= nBlockLen; + } + + return bResult; +} + +void MSCodec_Std97::GetDigestFromSalt( const sal_uInt8 pSaltData[16], sal_uInt8 pDigest[16] ) +{ + sal_uInt8 pBuffer[64]; + sal_uInt8 pDigestLocal[16]; + + // Decode SaltData into Buffer. + rtl_cipher_decode ( + m_hCipher, pSaltData, 16, pBuffer, sizeof(pBuffer)); + + // set the 129th bit to make the buffer 128-bit in length. + pBuffer[16] = 0x80; + + // erase the rest of the buffer with zeros. + (void)memset (pBuffer + 17, 0, sizeof(pBuffer) - 17); + + // set the 441st bit. + pBuffer[56] = 0x80; + + // Fill raw digest of Buffer into Digest. + rtl_digest_updateMD5 ( + m_hDigest, pBuffer, sizeof(pBuffer)); + rtl_digest_rawMD5 ( + m_hDigest, pDigestLocal, sizeof(pDigestLocal)); + + memcpy(pDigest, pDigestLocal, 16); +} + +void MSCodec_Std97::GetEncryptKey ( + const sal_uInt8 pSalt[16], + sal_uInt8 pSaltData[16], + sal_uInt8 pSaltDigest[16]) +{ + if (InitCipher(0)) + { + sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5]; + sal_uInt8 pBuffer[64]; + + rtl_cipher_encode ( + m_hCipher, pSalt, 16, pSaltData, sizeof(pBuffer)); + + (void)memcpy( pBuffer, pSalt, 16 ); + + pBuffer[16] = 0x80; + (void)memset (pBuffer + 17, 0, sizeof(pBuffer) - 17); + pBuffer[56] = 0x80; + + rtl_digest_updateMD5 ( + m_hDigest, pBuffer, sizeof(pBuffer)); + rtl_digest_rawMD5 ( + m_hDigest, pDigest, sizeof(pDigest)); + + rtl_cipher_encode ( + m_hCipher, pDigest, 16, pSaltDigest, 16); + + (void)memset (pBuffer, 0, sizeof(pBuffer)); + (void)memset (pDigest, 0, sizeof(pDigest)); + } +} + +void MSCodec_Std97::GetDocId( sal_uInt8 pDocId[16] ) +{ + if ( sizeof( m_pDocId ) == 16 ) + (void)memcpy( pDocId, m_pDocId, 16 ); +} + +// ============================================================================ + +} // namespace svx + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |