1 /***********************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_comphelper.hxx"
31 #include "comphelper/docpasswordhelper.hxx"
32 #include <com/sun/star/task/XInteractionHandler.hpp>
33 #include "comphelper/mediadescriptor.hxx"
36 #include <rtl/digest.h>
37 #include <rtl/random.h>
39 using ::rtl::OUString
;
40 using ::com::sun::star::uno::Sequence
;
41 using ::com::sun::star::uno::Exception
;
42 using ::com::sun::star::uno::Reference
;
43 using ::com::sun::star::uno::UNO_SET_THROW
;
44 using ::com::sun::star::task::PasswordRequestMode
;
45 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_ENTER
;
46 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_REENTER
;
47 using ::com::sun::star::task::XInteractionHandler
;
48 using ::com::sun::star::task::XInteractionRequest
;
50 using namespace ::com::sun::star
;
52 namespace comphelper
{
54 // ============================================================================
56 static uno::Sequence
< sal_Int8
> GeneratePBKDF2Hash( const ::rtl::OUString
& aPassword
, const uno::Sequence
< sal_Int8
>& aSalt
, sal_Int32 nCount
, sal_Int32 nHashLength
)
58 uno::Sequence
< sal_Int8
> aResult
;
60 if ( aPassword
.getLength() && aSalt
.getLength() && nCount
&& nHashLength
)
62 ::rtl::OString aBytePass
= ::rtl::OUStringToOString( aPassword
, RTL_TEXTENCODING_UTF8
);
63 aResult
.realloc( 16 );
64 rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8
* > ( aResult
.getArray() ),
66 reinterpret_cast < const sal_uInt8
* > ( aBytePass
.getStr() ),
67 aBytePass
.getLength(),
68 reinterpret_cast < const sal_uInt8
* > ( aSalt
.getConstArray() ),
76 // ============================================================================
78 IDocPasswordVerifier::~IDocPasswordVerifier()
82 // ============================================================================
83 uno::Sequence
< beans::PropertyValue
> DocPasswordHelper::GenerateNewModifyPasswordInfo( const ::rtl::OUString
& aPassword
)
85 uno::Sequence
< beans::PropertyValue
> aResult
;
87 uno::Sequence
< sal_Int8
> aSalt
= GenerateRandomByteSequence( 16 );
88 sal_Int32 nCount
= 1024;
90 uno::Sequence
< sal_Int8
> aNewHash
= GeneratePBKDF2Hash( aPassword
, aSalt
, nCount
, 16 );
91 if ( aNewHash
.getLength() )
94 aResult
[0].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "algorithm-name" ) );
95 aResult
[0].Value
<<= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PBKDF2" ) );
96 aResult
[1].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "salt" ) );
97 aResult
[1].Value
<<= aSalt
;
98 aResult
[2].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "iteration-count" ) );
99 aResult
[2].Value
<<= nCount
;
100 aResult
[3].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hash" ) );
101 aResult
[3].Value
<<= aNewHash
;
107 // ============================================================================
108 sal_Bool
DocPasswordHelper::IsModifyPasswordCorrect( const ::rtl::OUString
& aPassword
, const uno::Sequence
< beans::PropertyValue
>& aInfo
)
110 sal_Bool bResult
= sal_False
;
111 if ( aPassword
.getLength() && aInfo
.getLength() )
113 ::rtl::OUString sAlgorithm
;
114 uno::Sequence
< sal_Int8
> aSalt
;
115 uno::Sequence
< sal_Int8
> aHash
;
116 sal_Int32 nCount
= 0;
118 for ( sal_Int32 nInd
= 0; nInd
< aInfo
.getLength(); nInd
++ )
120 if ( aInfo
[nInd
].Name
.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "algorithm-name" ) ) ) )
121 aInfo
[nInd
].Value
>>= sAlgorithm
;
122 else if ( aInfo
[nInd
].Name
.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "salt" ) ) ) )
123 aInfo
[nInd
].Value
>>= aSalt
;
124 else if ( aInfo
[nInd
].Name
.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "iteration-count" ) ) ) )
125 aInfo
[nInd
].Value
>>= nCount
;
126 else if ( aInfo
[nInd
].Name
.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hash" ) ) ) )
127 aInfo
[nInd
].Value
>>= aHash
;
130 if ( sAlgorithm
.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PBKDF2" ) ) )
131 && aSalt
.getLength() && nCount
> 0 && aHash
.getLength() )
133 uno::Sequence
< sal_Int8
> aNewHash
= GeneratePBKDF2Hash( aPassword
, aSalt
, nCount
, aHash
.getLength() );
134 for ( sal_Int32 nInd
= 0; nInd
< aNewHash
.getLength() && nInd
< aHash
.getLength() && aNewHash
[nInd
] == aHash
[nInd
]; nInd
++ )
136 if ( nInd
== aNewHash
.getLength() - 1 && nInd
== aHash
.getLength() - 1 )
145 // ============================================================================
146 sal_uInt32
DocPasswordHelper::GetWordHashAsUINT32(
147 const ::rtl::OUString
& aUString
)
149 static sal_uInt16 pInitialCode
[] = {
167 static sal_uInt16 pEncryptionMatrix
[15][7] = {
168 { 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09}, // last-14
169 { 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF}, // last-13
170 { 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0}, // last-12
171 { 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40}, // last-11
172 { 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5}, // last-10
173 { 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A}, // last-9
174 { 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9}, // last-8
175 { 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0}, // last-7
176 { 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC}, // last-6
177 { 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10}, // last-5
178 { 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168}, // last-4
179 { 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C}, // last-3
180 { 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD}, // last-2
181 { 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC}, // last-1
182 { 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4} // last
185 sal_uInt32 nResult
= 0;
186 sal_uInt32 nLen
= aUString
.getLength();
193 sal_uInt16 nHighResult
= pInitialCode
[nLen
- 1];
194 sal_uInt16 nLowResult
= 0;
196 const sal_Unicode
* pStr
= aUString
.getStr();
197 for ( sal_uInt32 nInd
= 0; nInd
< nLen
; nInd
++ )
199 // NO Encoding during conversion!
200 // The specification says that the low byte should be used in case it is not NULL
201 char nHighChar
= (char)( pStr
[nInd
] >> 8 );
202 char nLowChar
= (char)( pStr
[nInd
] & 0xFF );
203 char nChar
= nLowChar
? nLowChar
: nHighChar
;
205 for ( int nMatrixInd
= 0; nMatrixInd
< 7; ++nMatrixInd
)
207 if ( ( nChar
& ( 1 << nMatrixInd
) ) != 0 )
208 nHighResult
= nHighResult
^ pEncryptionMatrix
[15 - nLen
+ nInd
][nMatrixInd
];
211 nLowResult
= ( ( ( nLowResult
>> 14 ) & 0x0001 ) | ( ( nLowResult
<< 1 ) & 0x7FFF ) ) ^ nChar
;
214 nLowResult
= (sal_uInt16
)( ( ( ( nLowResult
>> 14 ) & 0x001 ) | ( ( nLowResult
<< 1 ) & 0x7FF ) ) ^ nLen
^ 0xCE4B );
216 nResult
= ( nHighResult
<< 16 ) | nLowResult
;
222 // ============================================================================
223 Sequence
< sal_Int8
> DocPasswordHelper::GetWordHashAsSequence(
224 const ::rtl::OUString
& aUString
)
226 sal_uInt32 nHash
= GetWordHashAsUINT32( aUString
);
227 Sequence
< sal_Int8
> aResult( 4 );
228 aResult
[0] = ( nHash
>> 24 );
229 aResult
[1] = ( ( nHash
>> 16 ) & 0xFF );
230 aResult
[2] = ( ( nHash
>> 8 ) & 0xFF );
231 aResult
[3] = ( nHash
& 0xFF );
236 // ============================================================================
237 sal_uInt16
DocPasswordHelper::GetXLHashAsUINT16(
238 const ::rtl::OUString
& aUString
,
239 rtl_TextEncoding nEnc
)
241 sal_uInt16 nResult
= 0;
243 ::rtl::OString aString
= ::rtl::OUStringToOString( aUString
, nEnc
);
245 if ( aString
.getLength() && aString
.getLength() <= SAL_MAX_UINT16
)
247 for ( sal_Int32 nInd
= aString
.getLength() - 1; nInd
>= 0; nInd
-- )
249 nResult
= ( ( nResult
>> 14 ) & 0x01 ) | ( ( nResult
<< 1 ) & 0x7FFF );
250 nResult
^= aString
.getStr()[nInd
];
253 nResult
= ( ( nResult
>> 14 ) & 0x01 ) | ( ( nResult
<< 1 ) & 0x7FFF );
254 nResult
^= ( 0x8000 | ( 'N' << 8 ) | 'K' );
255 nResult
^= aString
.getLength();
261 // ============================================================================
262 Sequence
< sal_Int8
> DocPasswordHelper::GetXLHashAsSequence(
263 const ::rtl::OUString
& aUString
,
264 rtl_TextEncoding nEnc
)
266 sal_uInt16 nHash
= GetXLHashAsUINT16( aUString
, nEnc
);
267 Sequence
< sal_Int8
> aResult( 2 );
268 aResult
[0] = ( nHash
>> 8 );
269 aResult
[1] = ( nHash
& 0xFF );
274 // ============================================================================
275 /*static*/ uno::Sequence
< sal_Int8
> DocPasswordHelper::GenerateRandomByteSequence( sal_Int32 nLength
)
277 uno::Sequence
< sal_Int8
> aResult( nLength
);
280 osl_getSystemTime( &aTime
);
281 rtlRandomPool aRandomPool
= rtl_random_createPool ();
282 rtl_random_addBytes ( aRandomPool
, &aTime
, 8 );
283 rtl_random_getBytes ( aRandomPool
, aResult
.getArray(), nLength
);
284 rtl_random_destroyPool ( aRandomPool
);
290 // ============================================================================
291 /*static*/ uno::Sequence
< sal_Int8
> DocPasswordHelper::GenerateStd97Key( const ::rtl::OUString
& aPassword
, const uno::Sequence
< sal_Int8
>& aDocId
)
293 uno::Sequence
< sal_Int8
> aResultKey
;
294 if ( aPassword
.getLength() && aDocId
.getLength() == 16 )
296 sal_uInt16 pPassData
[16];
297 rtl_zeroMemory( pPassData
, sizeof(pPassData
) );
299 sal_Int32 nPassLen
= ::std::min
< sal_Int32
>( aPassword
.getLength(), 15 );
300 rtl_copyMemory( pPassData
, aPassword
.getStr(), nPassLen
* sizeof(pPassData
[0]) );
302 aResultKey
= GenerateStd97Key( pPassData
, aDocId
);
308 // ============================================================================
309 /*static*/ uno::Sequence
< sal_Int8
> DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData
[16], const uno::Sequence
< sal_Int8
>& aDocId
)
311 uno::Sequence
< sal_Int8
> aResultKey
;
312 if ( pPassData
[0] && aDocId
.getLength() == 16 )
314 sal_uInt8 pKeyData
[64];
315 rtl_zeroMemory( pKeyData
, sizeof(pKeyData
) );
319 // Fill PassData into KeyData.
320 for ( nInd
= 0; nInd
< 16 && pPassData
[nInd
]; nInd
++)
322 pKeyData
[2*nInd
] = sal::static_int_cast
< sal_uInt8
>( (pPassData
[nInd
] >> 0) & 0xff );
323 pKeyData
[2*nInd
+ 1] = sal::static_int_cast
< sal_uInt8
>( (pPassData
[nInd
] >> 8) & 0xff );
326 pKeyData
[2*nInd
] = 0x80;
327 pKeyData
[56] = sal::static_int_cast
< sal_uInt8
>( nInd
<< 4 );
329 // Fill raw digest of KeyData into KeyData.
330 rtlDigest hDigest
= rtl_digest_create ( rtl_Digest_AlgorithmMD5
);
331 (void)rtl_digest_updateMD5 (
332 hDigest
, pKeyData
, sizeof(pKeyData
));
333 (void)rtl_digest_rawMD5 (
334 hDigest
, pKeyData
, RTL_DIGEST_LENGTH_MD5
);
336 // Update digest with KeyData and Unique.
337 for ( nInd
= 0; nInd
< 16; nInd
++ )
339 rtl_digest_updateMD5( hDigest
, pKeyData
, 5 );
340 rtl_digest_updateMD5( hDigest
, (const sal_uInt8
*)aDocId
.getConstArray(), aDocId
.getLength() );
343 // Update digest with padding.
345 rtl_zeroMemory( pKeyData
+ 17, sizeof(pKeyData
) - 17 );
349 rtl_digest_updateMD5( hDigest
, &(pKeyData
[16]), sizeof(pKeyData
) - 16 );
351 // Fill raw digest of above updates
352 aResultKey
.realloc( RTL_DIGEST_LENGTH_MD5
);
353 rtl_digest_rawMD5 ( hDigest
, (sal_uInt8
*)aResultKey
.getArray(), aResultKey
.getLength() );
355 // Erase KeyData array and leave.
356 rtl_zeroMemory( pKeyData
, sizeof(pKeyData
) );
362 // ============================================================================
364 /*static*/ ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> DocPasswordHelper::requestAndVerifyDocPassword(
365 IDocPasswordVerifier
& rVerifier
,
366 const ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
>& rMediaEncData
,
367 const OUString
& rMediaPassword
,
368 const Reference
< XInteractionHandler
>& rxInteractHandler
,
369 const OUString
& rDocumentName
,
370 DocPasswordRequestType eRequestType
,
371 const ::std::vector
< OUString
>* pDefaultPasswords
,
372 bool* pbIsDefaultPassword
)
374 ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> aEncData
;
375 DocPasswordVerifierResult eResult
= DocPasswordVerifierResult_WRONG_PASSWORD
;
377 // first, try provided default passwords
378 if( pbIsDefaultPassword
)
379 *pbIsDefaultPassword
= false;
380 if( pDefaultPasswords
)
382 for( ::std::vector
< OUString
>::const_iterator aIt
= pDefaultPasswords
->begin(), aEnd
= pDefaultPasswords
->end(); (eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
) && (aIt
!= aEnd
); ++aIt
)
384 OSL_ENSURE( aIt
->getLength() > 0, "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" );
385 if( aIt
->getLength() > 0 )
387 eResult
= rVerifier
.verifyPassword( *aIt
, aEncData
);
388 if( pbIsDefaultPassword
)
389 *pbIsDefaultPassword
= eResult
== DocPasswordVerifierResult_OK
;
394 // try media encryption data (skip, if result is OK or ABORT)
395 if( eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
)
397 if( rMediaEncData
.getLength() > 0 )
399 eResult
= rVerifier
.verifyEncryptionData( rMediaEncData
);
400 if( eResult
== DocPasswordVerifierResult_OK
)
401 aEncData
= rMediaEncData
;
405 // try media password (skip, if result is OK or ABORT)
406 if( eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
)
408 if( rMediaPassword
.getLength() > 0 )
409 eResult
= rVerifier
.verifyPassword( rMediaPassword
, aEncData
);
412 // request a password (skip, if result is OK or ABORT)
413 if( (eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
) && rxInteractHandler
.is() ) try
415 PasswordRequestMode eRequestMode
= PasswordRequestMode_PASSWORD_ENTER
;
416 while( eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
)
418 DocPasswordRequest
* pRequest
= new DocPasswordRequest( eRequestType
, eRequestMode
, rDocumentName
);
419 Reference
< XInteractionRequest
> xRequest( pRequest
);
420 rxInteractHandler
->handle( xRequest
);
421 if( pRequest
->isPassword() )
423 if( pRequest
->getPassword().getLength() > 0 )
424 eResult
= rVerifier
.verifyPassword( pRequest
->getPassword(), aEncData
);
428 eResult
= DocPasswordVerifierResult_ABORT
;
430 eRequestMode
= PasswordRequestMode_PASSWORD_REENTER
;
437 return (eResult
== DocPasswordVerifierResult_OK
) ? aEncData
: uno::Sequence
< beans::NamedValue
>();
440 /*static*/ ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> DocPasswordHelper::requestAndVerifyDocPassword(
441 IDocPasswordVerifier
& rVerifier
,
442 MediaDescriptor
& rMediaDesc
,
443 DocPasswordRequestType eRequestType
,
444 const ::std::vector
< OUString
>* pDefaultPasswords
)
446 uno::Sequence
< beans::NamedValue
> aMediaEncData
= rMediaDesc
.getUnpackedValueOrDefault(
447 MediaDescriptor::PROP_ENCRYPTIONDATA(), uno::Sequence
< beans::NamedValue
>() );
448 OUString aMediaPassword
= rMediaDesc
.getUnpackedValueOrDefault(
449 MediaDescriptor::PROP_PASSWORD(), OUString() );
450 Reference
< XInteractionHandler
> xInteractHandler
= rMediaDesc
.getUnpackedValueOrDefault(
451 MediaDescriptor::PROP_INTERACTIONHANDLER(), Reference
< XInteractionHandler
>() );
452 OUString aDocumentName
= rMediaDesc
.getUnpackedValueOrDefault(
453 MediaDescriptor::PROP_URL(), OUString() );
455 bool bIsDefaultPassword
= false;
456 uno::Sequence
< beans::NamedValue
> aEncryptionData
= requestAndVerifyDocPassword(
457 rVerifier
, aMediaEncData
, aMediaPassword
, xInteractHandler
, aDocumentName
, eRequestType
, pDefaultPasswords
, &bIsDefaultPassword
);
459 rMediaDesc
.erase( MediaDescriptor::PROP_PASSWORD() );
460 rMediaDesc
.erase( MediaDescriptor::PROP_ENCRYPTIONDATA() );
462 // insert valid password into media descriptor (but not a default password)
463 if( (aEncryptionData
.getLength() > 0) && !bIsDefaultPassword
)
464 rMediaDesc
[ MediaDescriptor::PROP_ENCRYPTIONDATA() ] <<= aEncryptionData
;
466 return aEncryptionData
;
469 // ============================================================================
471 } // namespace comphelper