From 9ba9a27d921c28488444b3cd016dd5eb12c7525a Mon Sep 17 00:00:00 2001 From: Mikhail Voytenko Date: Thu, 4 Nov 2010 17:56:39 +0100 Subject: pl08: #163778# use EncryptionData from MediaDescriptor --- comphelper/inc/comphelper/docpasswordhelper.hxx | 63 +++++++++- comphelper/inc/comphelper/mediadescriptor.hxx | 1 + comphelper/inc/comphelper/storagehelper.hxx | 11 +- comphelper/source/misc/docpasswordhelper.cxx | 152 +++++++++++++++++++----- comphelper/source/misc/mediadescriptor.cxx | 6 + comphelper/source/misc/storagehelper.cxx | 52 +++++++- 6 files changed, 244 insertions(+), 41 deletions(-) diff --git a/comphelper/inc/comphelper/docpasswordhelper.hxx b/comphelper/inc/comphelper/docpasswordhelper.hxx index dbbb68372a07..7e9f06318a26 100644 --- a/comphelper/inc/comphelper/docpasswordhelper.hxx +++ b/comphelper/inc/comphelper/docpasswordhelper.hxx @@ -28,6 +28,7 @@ #ifndef COMPHELPER_DOCPASSWORDHELPR_HXX #define COMPHELPER_DOCPASSWORDHELPR_HXX +#include #include "comphelper/comphelperdllapi.h" #include #include "comphelper/docpasswordrequest.hxx" @@ -53,7 +54,7 @@ enum DocPasswordVerifierResult /** Base class for a password verifier used by the DocPasswordHelper class below. - Users have to implement the virtual function and pass an instance of the + Users have to implement the virtual functions and pass an instance of the verifier to one of the password request functions. */ class COMPHELPER_DLLPUBLIC IDocPasswordVerifier @@ -63,6 +64,14 @@ public: /** Will be called everytime a password needs to be verified. + @param rPassword + The password to be verified + + @param o_rEncryptionData + Output parameter, that is filled with the EncryptionData generated + from the password. The data is filled only if the validation was + successful. + @return The result of the verification. - DocPasswordVerifierResult_OK, if and only if the passed password is valid and can be used to process the related document. @@ -72,7 +81,23 @@ public: occured while password verification. The password request loop will be aborted. */ - virtual DocPasswordVerifierResult verifyPassword( const ::rtl::OUString& rPassword ) = 0; + virtual DocPasswordVerifierResult verifyPassword( const ::rtl::OUString& rPassword, ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& o_rEncryptionData ) = 0; + + /** Will be called everytime an encryption data needs to be verified. + + @param rEncryptionData + The data will be validated + + @return The result of the verification. + - DocPasswordVerifierResult_OK, if and only if the passed encryption data + is valid and can be used to process the related document. + - DocPasswordVerifierResult_WRONG_PASSWORD, if the encryption data is + wrong. + - DocPasswordVerifierResult_ABORT, if an unrecoverable error + occured while data verification. The password request loop + will be aborted. + */ + virtual DocPasswordVerifierResult verifyEncryptionData( const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& o_rEncryptionData ) = 0; }; @@ -195,6 +220,35 @@ public: // ------------------------------------------------------------------------ + /** This helper function generates a random sequence of bytes of + requested length. + */ + + static ::com::sun::star::uno::Sequence< sal_Int8 > GenerateRandomByteSequence( + sal_Int32 nLength ); + + // ------------------------------------------------------------------------ + + /** This helper function generates a byte sequence representing the + key digest value used by MSCodec_Std97 codec. + */ + + static ::com::sun::star::uno::Sequence< sal_Int8 > GenerateStd97Key( + const ::rtl::OUString& aPassword, + const ::com::sun::star::uno::Sequence< sal_Int8 >& aDocId ); + + // ------------------------------------------------------------------------ + + /** This helper function generates a byte sequence representing the + key digest value used by MSCodec_Std97 codec. + */ + + static ::com::sun::star::uno::Sequence< sal_Int8 > GenerateStd97Key( + const sal_uInt16 pPassData[16], + const ::com::sun::star::uno::Sequence< sal_Int8 >& aDocId ); + + // ------------------------------------------------------------------------ + /** This helper function tries to request and verify a password to load a protected document. @@ -248,8 +302,9 @@ public: passed password verifier. If empty, no valid password has been found, or the user has chossen to cancel password input. */ - static ::rtl::OUString requestAndVerifyDocPassword( + static ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > requestAndVerifyDocPassword( IDocPasswordVerifier& rVerifier, + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& rMediaEncData, const ::rtl::OUString& rMediaPassword, const ::com::sun::star::uno::Reference< ::com::sun::star::task::XInteractionHandler >& rxInteractHandler, @@ -300,7 +355,7 @@ public: passed password verifier. If empty, no valid password has been found, or the user has chossen to cancel password input. */ - static ::rtl::OUString requestAndVerifyDocPassword( + static ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > requestAndVerifyDocPassword( IDocPasswordVerifier& rVerifier, MediaDescriptor& rMediaDesc, DocPasswordRequestType eRequestType, diff --git a/comphelper/inc/comphelper/mediadescriptor.hxx b/comphelper/inc/comphelper/mediadescriptor.hxx index 7d2333045390..01fa8059b284 100644 --- a/comphelper/inc/comphelper/mediadescriptor.hxx +++ b/comphelper/inc/comphelper/mediadescriptor.hxx @@ -78,6 +78,7 @@ class COMPHELPER_DLLPUBLIC MediaDescriptor : public SequenceAsHashMap static const ::rtl::OUString& PROP_DEEPDETECTION(); static const ::rtl::OUString& PROP_DETECTSERVICE(); static const ::rtl::OUString& PROP_DOCUMENTSERVICE(); + static const ::rtl::OUString& PROP_ENCRYPTIONDATA(); static const ::rtl::OUString& PROP_EXTENSION(); static const ::rtl::OUString& PROP_FILENAME(); static const ::rtl::OUString& PROP_FILTERNAME(); diff --git a/comphelper/inc/comphelper/storagehelper.hxx b/comphelper/inc/comphelper/storagehelper.hxx index b7e5704c4d68..9d44b42e9514 100644 --- a/comphelper/inc/comphelper/storagehelper.hxx +++ b/comphelper/inc/comphelper/storagehelper.hxx @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +44,9 @@ #define ZIP_STORAGE_FORMAT_STRING ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ZipFormat" ) ) #define OFOPXML_STORAGE_FORMAT_STRING ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "OFOPXMLFormat" ) ) +#define PACKAGE_ENCRYPTIONDATA_SHA1UTF8 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PackageSHA1UTF8EncryptionKey" ) ) +#define PACKAGE_ENCRYPTIONDATA_SHA1MS1252 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PackageSHA1MS1252EncryptionKey" ) ) + namespace comphelper { class COMPHELPER_DLLPUBLIC OStorageHelper @@ -112,9 +116,9 @@ public: = ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >() ) throw ( ::com::sun::star::uno::Exception ); - static void SetCommonStoragePassword( + static void SetCommonStorageEncryptionData( const ::com::sun::star::uno::Reference< ::com::sun::star::embed::XStorage >& xStorage, - const ::rtl::OUString& aPass ) + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& aEncryptionData ) throw ( ::com::sun::star::uno::Exception ); // the following method supports only storages of OOo formats @@ -159,6 +163,9 @@ public: sal_Bool bRepairStorage = sal_False ) throw ( ::com::sun::star::uno::Exception ); + static ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > + CreatePackageEncryptionData( const ::rtl::OUString& aPassword ); + static sal_Bool IsValidZipEntryFileName( const ::rtl::OUString& aName, sal_Bool bSlashAllowed ); static sal_Bool IsValidZipEntryFileName( const sal_Unicode *pChar, sal_Int32 nLength, sal_Bool bSlashAllowed ); diff --git a/comphelper/source/misc/docpasswordhelper.cxx b/comphelper/source/misc/docpasswordhelper.cxx index 37941352ae28..697f221d9c80 100644 --- a/comphelper/source/misc/docpasswordhelper.cxx +++ b/comphelper/source/misc/docpasswordhelper.cxx @@ -84,16 +84,9 @@ uno::Sequence< beans::PropertyValue > DocPasswordHelper::GenerateNewModifyPasswo { uno::Sequence< beans::PropertyValue > aResult; - uno::Sequence< sal_Int8 > aSalt( 16 ); + uno::Sequence< sal_Int8 > aSalt = GenerateRandomByteSequence( 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() ) { @@ -108,9 +101,6 @@ uno::Sequence< beans::PropertyValue > DocPasswordHelper::GenerateNewModifyPasswo aResult[3].Value <<= aNewHash; } - // Clean up random pool memory - rtl_random_destroyPool ( aRandomPool ); - return aResult; } @@ -282,9 +272,98 @@ Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( } // ============================================================================ +/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateRandomByteSequence( sal_Int32 nLength ) +{ + uno::Sequence< sal_Int8 > aResult( nLength ); + + TimeValue aTime; + osl_getSystemTime( &aTime ); + rtlRandomPool aRandomPool = rtl_random_createPool (); + rtl_random_addBytes ( aRandomPool, &aTime, 8 ); + rtl_random_getBytes ( aRandomPool, aResult.getArray(), nLength ); + rtl_random_destroyPool ( aRandomPool ); -/*static*/ OUString DocPasswordHelper::requestAndVerifyDocPassword( + return aResult; +} + + +// ============================================================================ +/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const ::rtl::OUString& aPassword, const uno::Sequence< sal_Int8 >& aDocId ) +{ + uno::Sequence< sal_Int8 > aResultKey; + if ( aPassword.getLength() && aDocId.getLength() == 16 ) + { + sal_uInt16 pPassData[16]; + memset( pPassData, 0, sizeof(pPassData) ); + + sal_Int32 nPassLen = ::std::min< sal_Int32 >( aPassword.getLength(), 15 ); + (void)memcpy( pPassData, aPassword.getStr(), nPassLen * sizeof(pPassData[0]) ); + + aResultKey = GenerateStd97Key( pPassData, aDocId ); + } + + return aResultKey; +} + +// ============================================================================ +/*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const uno::Sequence< sal_Int8 >& aDocId ) +{ + uno::Sequence< sal_Int8 > aResultKey; + if ( pPassData[0] && aDocId.getLength() == 16 ) + { + sal_uInt8 pKeyData[64]; + (void)memset( pKeyData, 0, sizeof(pKeyData) ); + + sal_Int32 nInd = 0; + + // Fill PassData into KeyData. + for ( nInd = 0; nInd < 16 && pPassData[nInd]; nInd++) + { + pKeyData[2*nInd] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 0) & 0xff ); + pKeyData[2*nInd + 1] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 8) & 0xff ); + } + + pKeyData[2*nInd] = 0x80; + pKeyData[56] = sal::static_int_cast< sal_uInt8 >( nInd << 4 ); + + // Fill raw digest of KeyData into KeyData. + rtlDigest hDigest = rtl_digest_create ( rtl_Digest_AlgorithmMD5 ); + (void)rtl_digest_updateMD5 ( + hDigest, pKeyData, sizeof(pKeyData)); + (void)rtl_digest_rawMD5 ( + hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5); + + // Update digest with KeyData and Unique. + for ( nInd = 0; nInd < 16; nInd++ ) + { + rtl_digest_updateMD5( hDigest, pKeyData, 5 ); + rtl_digest_updateMD5( hDigest, (const sal_uInt8*)aDocId.getConstArray(), aDocId.getLength() ); + } + + // Update digest with padding. + pKeyData[16] = 0x80; + (void)memset (pKeyData + 17, 0, sizeof(pKeyData) - 17); + pKeyData[56] = 0x80; + pKeyData[57] = 0x0a; + + rtl_digest_updateMD5( hDigest, &(pKeyData[16]), sizeof(pKeyData) - 16 ); + + // Fill raw digest of above updates + aResultKey.realloc( RTL_DIGEST_LENGTH_MD5 ); + rtl_digest_rawMD5 ( hDigest, (sal_uInt8*)aResultKey.getArray(), aResultKey.getLength() ); + + // Erase KeyData array and leave. + (void)memset (pKeyData, 0, sizeof(pKeyData)); + } + + return aResultKey; +} + +// ============================================================================ + +/*static*/ ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > DocPasswordHelper::requestAndVerifyDocPassword( IDocPasswordVerifier& rVerifier, + const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& rMediaEncData, const OUString& rMediaPassword, const Reference< XInteractionHandler >& rxInteractHandler, const OUString& rDocumentName, @@ -292,7 +371,7 @@ Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( const ::std::vector< OUString >* pDefaultPasswords, bool* pbIsDefaultPassword ) { - OUString aPassword; + ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > aEncData; DocPasswordVerifierResult eResult = DocPasswordVerifierResult_WRONG_PASSWORD; // first, try provided default passwords @@ -302,23 +381,32 @@ Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( { for( ::std::vector< OUString >::const_iterator aIt = pDefaultPasswords->begin(), aEnd = pDefaultPasswords->end(); (eResult == DocPasswordVerifierResult_WRONG_PASSWORD) && (aIt != aEnd); ++aIt ) { - aPassword = *aIt; - OSL_ENSURE( aPassword.getLength() > 0, "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" ); - if( aPassword.getLength() > 0 ) + OSL_ENSURE( aIt->getLength() > 0, "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" ); + if( aIt->getLength() > 0 ) { - eResult = rVerifier.verifyPassword( aPassword ); + eResult = rVerifier.verifyPassword( *aIt, aEncData ); if( pbIsDefaultPassword ) *pbIsDefaultPassword = eResult == DocPasswordVerifierResult_OK; } } } + // try media encryption data (skip, if result is OK or ABORT) + if( eResult == DocPasswordVerifierResult_WRONG_PASSWORD ) + { + if( rMediaEncData.getLength() > 0 ) + { + eResult = rVerifier.verifyEncryptionData( rMediaEncData ); + if( eResult == DocPasswordVerifierResult_OK ) + aEncData = rMediaEncData; + } + } + // try media password (skip, if result is OK or ABORT) if( eResult == DocPasswordVerifierResult_WRONG_PASSWORD ) { - aPassword = rMediaPassword; - if( aPassword.getLength() > 0 ) - eResult = rVerifier.verifyPassword( aPassword ); + if( rMediaPassword.getLength() > 0 ) + eResult = rVerifier.verifyPassword( rMediaPassword, aEncData ); } // request a password (skip, if result is OK or ABORT) @@ -332,9 +420,8 @@ Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( rxInteractHandler->handle( xRequest ); if( pRequest->isPassword() ) { - aPassword = pRequest->getPassword(); - if( aPassword.getLength() > 0 ) - eResult = rVerifier.verifyPassword( aPassword ); + if( pRequest->getPassword().getLength() > 0 ) + eResult = rVerifier.verifyPassword( pRequest->getPassword(), aEncData ); } else { @@ -347,15 +434,17 @@ Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( { } - return (eResult == DocPasswordVerifierResult_OK) ? aPassword : OUString(); + return (eResult == DocPasswordVerifierResult_OK) ? aEncData : uno::Sequence< beans::NamedValue >(); } -/*static*/ OUString DocPasswordHelper::requestAndVerifyDocPassword( +/*static*/ ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > DocPasswordHelper::requestAndVerifyDocPassword( IDocPasswordVerifier& rVerifier, MediaDescriptor& rMediaDesc, DocPasswordRequestType eRequestType, const ::std::vector< OUString >* pDefaultPasswords ) { + uno::Sequence< beans::NamedValue > aMediaEncData = rMediaDesc.getUnpackedValueOrDefault( + MediaDescriptor::PROP_ENCRYPTIONDATA(), uno::Sequence< beans::NamedValue >() ); OUString aMediaPassword = rMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_PASSWORD(), OUString() ); Reference< XInteractionHandler > xInteractHandler = rMediaDesc.getUnpackedValueOrDefault( @@ -364,14 +453,17 @@ Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( MediaDescriptor::PROP_URL(), OUString() ); bool bIsDefaultPassword = false; - OUString aPassword = requestAndVerifyDocPassword( - rVerifier, aMediaPassword, xInteractHandler, aDocumentName, eRequestType, pDefaultPasswords, &bIsDefaultPassword ); + uno::Sequence< beans::NamedValue > aEncryptionData = requestAndVerifyDocPassword( + rVerifier, aMediaEncData, aMediaPassword, xInteractHandler, aDocumentName, eRequestType, pDefaultPasswords, &bIsDefaultPassword ); + + rMediaDesc.erase( MediaDescriptor::PROP_PASSWORD() ); + rMediaDesc.erase( MediaDescriptor::PROP_ENCRYPTIONDATA() ); // insert valid password into media descriptor (but not a default password) - if( (aPassword.getLength() > 0) && !bIsDefaultPassword ) - rMediaDesc[ MediaDescriptor::PROP_PASSWORD() ] <<= aPassword; + if( (aEncryptionData.getLength() > 0) && !bIsDefaultPassword ) + rMediaDesc[ MediaDescriptor::PROP_ENCRYPTIONDATA() ] <<= aEncryptionData; - return aPassword; + return aEncryptionData; } // ============================================================================ diff --git a/comphelper/source/misc/mediadescriptor.cxx b/comphelper/source/misc/mediadescriptor.cxx index 9e02afe8c56c..143f8ba4dfa2 100644 --- a/comphelper/source/misc/mediadescriptor.cxx +++ b/comphelper/source/misc/mediadescriptor.cxx @@ -112,6 +112,12 @@ const ::rtl::OUString& MediaDescriptor::PROP_DOCUMENTSERVICE() return sProp; } +const ::rtl::OUString& MediaDescriptor::PROP_ENCRYPTIONDATA() +{ + static const ::rtl::OUString sProp(RTL_CONSTASCII_USTRINGPARAM("EncryptionData")); + return sProp; +} + const ::rtl::OUString& MediaDescriptor::PROP_EXTENSION() { static const ::rtl::OUString sProp(RTL_CONSTASCII_USTRINGPARAM("Extension")); diff --git a/comphelper/source/misc/storagehelper.cxx b/comphelper/source/misc/storagehelper.cxx index db5ba71cd876..60ffa965fcf1 100644 --- a/comphelper/source/misc/storagehelper.cxx +++ b/comphelper/source/misc/storagehelper.cxx @@ -28,12 +28,15 @@ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_comphelper.hxx" #include -#include +#include #include #include #include +#include #include +#include + #include #include @@ -236,16 +239,16 @@ uno::Reference< io::XInputStream > OStorageHelper::GetInputStreamFromURL( } // ---------------------------------------------------------------------- -void OStorageHelper::SetCommonStoragePassword( +void OStorageHelper::SetCommonStorageEncryptionData( const uno::Reference< embed::XStorage >& xStorage, - const ::rtl::OUString& aPass ) + const uno::Sequence< beans::NamedValue >& aEncryptionData ) throw ( uno::Exception ) { - uno::Reference< embed::XEncryptionProtectedSource > xEncrSet( xStorage, uno::UNO_QUERY ); + uno::Reference< embed::XEncryptionProtectedSource2 > xEncrSet( xStorage, uno::UNO_QUERY ); if ( !xEncrSet.is() ) throw io::IOException(); // TODO - xEncrSet->setEncryptionPassword( aPass ); + xEncrSet->setEncryptionData( aEncryptionData ); } // ---------------------------------------------------------------------- @@ -418,6 +421,45 @@ uno::Reference< embed::XStorage > OStorageHelper::GetStorageOfFormatFromStream( return xTempStorage; } +// ---------------------------------------------------------------------- +uno::Sequence< beans::NamedValue > OStorageHelper::CreatePackageEncryptionData( const ::rtl::OUString& aPassword ) +{ + // TODO/LATER: Should not the method be part of DocPasswordHelper? + uno::Sequence< beans::NamedValue > aEncryptionData; + if ( aPassword.getLength() ) + { + // MS_1252 encoding was used for SO60 document format password encoding, + // this encoding supports only a minor subset of nonascii characters, + // but for compatibility reasons it has to be used for old document formats + aEncryptionData.realloc( 2 ); + aEncryptionData[0].Name = PACKAGE_ENCRYPTIONDATA_SHA1UTF8; + aEncryptionData[1].Name = PACKAGE_ENCRYPTIONDATA_SHA1MS1252; + + rtl_TextEncoding pEncoding[2] = { RTL_TEXTENCODING_UTF8, RTL_TEXTENCODING_MS_1252 }; + + for ( sal_Int32 nInd = 0; nInd < 2; nInd++ ) + { + ::rtl::OString aByteStrPass = ::rtl::OUStringToOString( aPassword, pEncoding[nInd] ); + + sal_uInt8 pBuffer[RTL_DIGEST_LENGTH_SHA1]; + rtlDigestError nError = rtl_digest_SHA1( aByteStrPass.getStr(), + aByteStrPass.getLength(), + pBuffer, + RTL_DIGEST_LENGTH_SHA1 ); + + if ( nError != rtl_Digest_E_None ) + { + aEncryptionData.realloc( 0 ); + break; + } + + aEncryptionData[nInd].Value <<= uno::Sequence< sal_Int8 >( (sal_Int8*)pBuffer, RTL_DIGEST_LENGTH_SHA1 ); + } + } + + return aEncryptionData; +} + // ---------------------------------------------------------------------- sal_Bool OStorageHelper::IsValidZipEntryFileName( const ::rtl::OUString& aName, sal_Bool bSlashAllowed ) { -- cgit v1.2.3