1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "comphelper/docpasswordhelper.hxx"
22 #include <com/sun/star/task/XInteractionHandler.hpp>
23 #include "comphelper/mediadescriptor.hxx"
26 #include <rtl/digest.h>
27 #include <rtl/random.h>
30 using ::rtl::OUString
;
31 using ::com::sun::star::uno::Sequence
;
32 using ::com::sun::star::uno::Exception
;
33 using ::com::sun::star::uno::Reference
;
34 using ::com::sun::star::uno::UNO_SET_THROW
;
35 using ::com::sun::star::task::PasswordRequestMode
;
36 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_ENTER
;
37 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_REENTER
;
38 using ::com::sun::star::task::XInteractionHandler
;
39 using ::com::sun::star::task::XInteractionRequest
;
41 using namespace ::com::sun::star
;
43 namespace comphelper
{
45 // ============================================================================
47 static uno::Sequence
< sal_Int8
> GeneratePBKDF2Hash( const ::rtl::OUString
& aPassword
, const uno::Sequence
< sal_Int8
>& aSalt
, sal_Int32 nCount
, sal_Int32 nHashLength
)
49 uno::Sequence
< sal_Int8
> aResult
;
51 if ( !aPassword
.isEmpty() && aSalt
.getLength() && nCount
&& nHashLength
)
53 ::rtl::OString aBytePass
= ::rtl::OUStringToOString( aPassword
, RTL_TEXTENCODING_UTF8
);
54 aResult
.realloc( 16 );
55 rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8
* > ( aResult
.getArray() ),
57 reinterpret_cast < const sal_uInt8
* > ( aBytePass
.getStr() ),
58 aBytePass
.getLength(),
59 reinterpret_cast < const sal_uInt8
* > ( aSalt
.getConstArray() ),
67 // ============================================================================
69 IDocPasswordVerifier::~IDocPasswordVerifier()
73 // ============================================================================
74 uno::Sequence
< beans::PropertyValue
> DocPasswordHelper::GenerateNewModifyPasswordInfo( const ::rtl::OUString
& aPassword
)
76 uno::Sequence
< beans::PropertyValue
> aResult
;
78 uno::Sequence
< sal_Int8
> aSalt
= GenerateRandomByteSequence( 16 );
79 sal_Int32 nCount
= 1024;
81 uno::Sequence
< sal_Int8
> aNewHash
= GeneratePBKDF2Hash( aPassword
, aSalt
, nCount
, 16 );
82 if ( aNewHash
.getLength() )
85 aResult
[0].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "algorithm-name" ) );
86 aResult
[0].Value
<<= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PBKDF2" ) );
87 aResult
[1].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "salt" ) );
88 aResult
[1].Value
<<= aSalt
;
89 aResult
[2].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "iteration-count" ) );
90 aResult
[2].Value
<<= nCount
;
91 aResult
[3].Name
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hash" ) );
92 aResult
[3].Value
<<= aNewHash
;
98 // ============================================================================
99 sal_Bool
DocPasswordHelper::IsModifyPasswordCorrect( const ::rtl::OUString
& aPassword
, const uno::Sequence
< beans::PropertyValue
>& aInfo
)
101 sal_Bool bResult
= sal_False
;
102 if ( !aPassword
.isEmpty() && aInfo
.getLength() )
104 ::rtl::OUString sAlgorithm
;
105 uno::Sequence
< sal_Int8
> aSalt
;
106 uno::Sequence
< sal_Int8
> aHash
;
107 sal_Int32 nCount
= 0;
109 for ( sal_Int32 nInd
= 0; nInd
< aInfo
.getLength(); nInd
++ )
111 if ( aInfo
[nInd
].Name
== "algorithm-name" )
112 aInfo
[nInd
].Value
>>= sAlgorithm
;
113 else if ( aInfo
[nInd
].Name
== "salt" )
114 aInfo
[nInd
].Value
>>= aSalt
;
115 else if ( aInfo
[nInd
].Name
== "iteration-count" )
116 aInfo
[nInd
].Value
>>= nCount
;
117 else if ( aInfo
[nInd
].Name
== "hash" )
118 aInfo
[nInd
].Value
>>= aHash
;
121 if ( sAlgorithm
== "PBKDF2" && aSalt
.getLength() && nCount
> 0 && aHash
.getLength() )
123 uno::Sequence
< sal_Int8
> aNewHash
= GeneratePBKDF2Hash( aPassword
, aSalt
, nCount
, aHash
.getLength() );
124 for ( sal_Int32 nInd
= 0; nInd
< aNewHash
.getLength() && nInd
< aHash
.getLength() && aNewHash
[nInd
] == aHash
[nInd
]; nInd
++ )
126 if ( nInd
== aNewHash
.getLength() - 1 && nInd
== aHash
.getLength() - 1 )
135 // ============================================================================
136 sal_uInt32
DocPasswordHelper::GetWordHashAsUINT32(
137 const ::rtl::OUString
& aUString
)
139 static sal_uInt16 pInitialCode
[] = {
157 static sal_uInt16 pEncryptionMatrix
[15][7] = {
158 { 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09}, // last-14
159 { 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF}, // last-13
160 { 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0}, // last-12
161 { 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40}, // last-11
162 { 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5}, // last-10
163 { 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A}, // last-9
164 { 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9}, // last-8
165 { 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0}, // last-7
166 { 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC}, // last-6
167 { 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10}, // last-5
168 { 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168}, // last-4
169 { 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C}, // last-3
170 { 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD}, // last-2
171 { 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC}, // last-1
172 { 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4} // last
175 sal_uInt32 nResult
= 0;
176 sal_uInt32 nLen
= aUString
.getLength();
183 sal_uInt16 nHighResult
= pInitialCode
[nLen
- 1];
184 sal_uInt16 nLowResult
= 0;
186 const sal_Unicode
* pStr
= aUString
.getStr();
187 for ( sal_uInt32 nInd
= 0; nInd
< nLen
; nInd
++ )
189 // NO Encoding during conversion!
190 // The specification says that the low byte should be used in case it is not NULL
191 char nHighChar
= (char)( pStr
[nInd
] >> 8 );
192 char nLowChar
= (char)( pStr
[nInd
] & 0xFF );
193 char nChar
= nLowChar
? nLowChar
: nHighChar
;
195 for ( int nMatrixInd
= 0; nMatrixInd
< 7; ++nMatrixInd
)
197 if ( ( nChar
& ( 1 << nMatrixInd
) ) != 0 )
198 nHighResult
= nHighResult
^ pEncryptionMatrix
[15 - nLen
+ nInd
][nMatrixInd
];
201 nLowResult
= ( ( ( nLowResult
>> 14 ) & 0x0001 ) | ( ( nLowResult
<< 1 ) & 0x7FFF ) ) ^ nChar
;
204 nLowResult
= (sal_uInt16
)( ( ( ( nLowResult
>> 14 ) & 0x001 ) | ( ( nLowResult
<< 1 ) & 0x7FF ) ) ^ nLen
^ 0xCE4B );
206 nResult
= ( nHighResult
<< 16 ) | nLowResult
;
212 // ============================================================================
213 sal_uInt16
DocPasswordHelper::GetXLHashAsUINT16(
214 const ::rtl::OUString
& aUString
,
215 rtl_TextEncoding nEnc
)
217 sal_uInt16 nResult
= 0;
219 ::rtl::OString aString
= ::rtl::OUStringToOString( aUString
, nEnc
);
221 if ( !aString
.isEmpty() && aString
.getLength() <= SAL_MAX_UINT16
)
223 for ( sal_Int32 nInd
= aString
.getLength() - 1; nInd
>= 0; nInd
-- )
225 nResult
= ( ( nResult
>> 14 ) & 0x01 ) | ( ( nResult
<< 1 ) & 0x7FFF );
226 nResult
^= aString
.getStr()[nInd
];
229 nResult
= ( ( nResult
>> 14 ) & 0x01 ) | ( ( nResult
<< 1 ) & 0x7FFF );
230 nResult
^= ( 0x8000 | ( 'N' << 8 ) | 'K' );
231 nResult
^= aString
.getLength();
237 // ============================================================================
238 Sequence
< sal_Int8
> DocPasswordHelper::GetXLHashAsSequence(
239 const ::rtl::OUString
& aUString
,
240 rtl_TextEncoding nEnc
)
242 sal_uInt16 nHash
= GetXLHashAsUINT16( aUString
, nEnc
);
243 Sequence
< sal_Int8
> aResult( 2 );
244 aResult
[0] = ( nHash
>> 8 );
245 aResult
[1] = ( nHash
& 0xFF );
250 // ============================================================================
251 /*static*/ uno::Sequence
< sal_Int8
> DocPasswordHelper::GenerateRandomByteSequence( sal_Int32 nLength
)
253 uno::Sequence
< sal_Int8
> aResult( nLength
);
256 osl_getSystemTime( &aTime
);
257 rtlRandomPool aRandomPool
= rtl_random_createPool ();
258 rtl_random_addBytes ( aRandomPool
, &aTime
, 8 );
259 rtl_random_getBytes ( aRandomPool
, aResult
.getArray(), nLength
);
260 rtl_random_destroyPool ( aRandomPool
);
266 // ============================================================================
267 /*static*/ uno::Sequence
< sal_Int8
> DocPasswordHelper::GenerateStd97Key( const ::rtl::OUString
& aPassword
, const uno::Sequence
< sal_Int8
>& aDocId
)
269 uno::Sequence
< sal_Int8
> aResultKey
;
270 if ( !aPassword
.isEmpty() && aDocId
.getLength() == 16 )
272 sal_uInt16 pPassData
[16];
273 memset( pPassData
, 0, sizeof(pPassData
) );
275 sal_Int32 nPassLen
= ::std::min
< sal_Int32
>( aPassword
.getLength(), 15 );
276 memcpy( pPassData
, aPassword
.getStr(), nPassLen
* sizeof(pPassData
[0]) );
278 aResultKey
= GenerateStd97Key( pPassData
, aDocId
);
284 // ============================================================================
285 /*static*/ uno::Sequence
< sal_Int8
> DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData
[16], const uno::Sequence
< sal_Int8
>& aDocId
)
287 uno::Sequence
< sal_Int8
> aResultKey
;
288 if ( pPassData
[0] && aDocId
.getLength() == 16 )
290 sal_uInt8 pKeyData
[64];
291 memset( pKeyData
, 0, sizeof(pKeyData
) );
295 // Fill PassData into KeyData.
296 for ( nInd
= 0; nInd
< 16 && pPassData
[nInd
]; nInd
++)
298 pKeyData
[2*nInd
] = sal::static_int_cast
< sal_uInt8
>( (pPassData
[nInd
] >> 0) & 0xff );
299 pKeyData
[2*nInd
+ 1] = sal::static_int_cast
< sal_uInt8
>( (pPassData
[nInd
] >> 8) & 0xff );
302 pKeyData
[2*nInd
] = 0x80;
303 pKeyData
[56] = sal::static_int_cast
< sal_uInt8
>( nInd
<< 4 );
305 // Fill raw digest of KeyData into KeyData.
306 rtlDigest hDigest
= rtl_digest_create ( rtl_Digest_AlgorithmMD5
);
307 (void)rtl_digest_updateMD5 (
308 hDigest
, pKeyData
, sizeof(pKeyData
));
309 (void)rtl_digest_rawMD5 (
310 hDigest
, pKeyData
, RTL_DIGEST_LENGTH_MD5
);
312 // Update digest with KeyData and Unique.
313 for ( nInd
= 0; nInd
< 16; nInd
++ )
315 rtl_digest_updateMD5( hDigest
, pKeyData
, 5 );
316 rtl_digest_updateMD5( hDigest
, (const sal_uInt8
*)aDocId
.getConstArray(), aDocId
.getLength() );
319 // Update digest with padding.
321 memset( pKeyData
+ 17, 0, sizeof(pKeyData
) - 17 );
325 rtl_digest_updateMD5( hDigest
, &(pKeyData
[16]), sizeof(pKeyData
) - 16 );
327 // Fill raw digest of above updates
328 aResultKey
.realloc( RTL_DIGEST_LENGTH_MD5
);
329 rtl_digest_rawMD5 ( hDigest
, (sal_uInt8
*)aResultKey
.getArray(), aResultKey
.getLength() );
331 // Erase KeyData array and leave.
332 memset( pKeyData
, 0, sizeof(pKeyData
) );
338 // ============================================================================
340 /*static*/ ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> DocPasswordHelper::requestAndVerifyDocPassword(
341 IDocPasswordVerifier
& rVerifier
,
342 const ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
>& rMediaEncData
,
343 const OUString
& rMediaPassword
,
344 const Reference
< XInteractionHandler
>& rxInteractHandler
,
345 const OUString
& rDocumentName
,
346 DocPasswordRequestType eRequestType
,
347 const ::std::vector
< OUString
>* pDefaultPasswords
,
348 bool* pbIsDefaultPassword
)
350 ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> aEncData
;
351 DocPasswordVerifierResult eResult
= DocPasswordVerifierResult_WRONG_PASSWORD
;
353 // first, try provided default passwords
354 if( pbIsDefaultPassword
)
355 *pbIsDefaultPassword
= false;
356 if( pDefaultPasswords
)
358 for( ::std::vector
< OUString
>::const_iterator aIt
= pDefaultPasswords
->begin(), aEnd
= pDefaultPasswords
->end(); (eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
) && (aIt
!= aEnd
); ++aIt
)
360 OSL_ENSURE( !aIt
->isEmpty(), "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" );
361 if( !aIt
->isEmpty() )
363 eResult
= rVerifier
.verifyPassword( *aIt
, aEncData
);
364 if( pbIsDefaultPassword
)
365 *pbIsDefaultPassword
= eResult
== DocPasswordVerifierResult_OK
;
370 // try media encryption data (skip, if result is OK or ABORT)
371 if( eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
)
373 if( rMediaEncData
.getLength() > 0 )
375 eResult
= rVerifier
.verifyEncryptionData( rMediaEncData
);
376 if( eResult
== DocPasswordVerifierResult_OK
)
377 aEncData
= rMediaEncData
;
381 // try media password (skip, if result is OK or ABORT)
382 if( eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
)
384 if( !rMediaPassword
.isEmpty() )
385 eResult
= rVerifier
.verifyPassword( rMediaPassword
, aEncData
);
388 // request a password (skip, if result is OK or ABORT)
389 if( (eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
) && rxInteractHandler
.is() ) try
391 PasswordRequestMode eRequestMode
= PasswordRequestMode_PASSWORD_ENTER
;
392 while( eResult
== DocPasswordVerifierResult_WRONG_PASSWORD
)
394 DocPasswordRequest
* pRequest
= new DocPasswordRequest( eRequestType
, eRequestMode
, rDocumentName
);
395 Reference
< XInteractionRequest
> xRequest( pRequest
);
396 rxInteractHandler
->handle( xRequest
);
397 if( pRequest
->isPassword() )
399 if( !pRequest
->getPassword().isEmpty() )
400 eResult
= rVerifier
.verifyPassword( pRequest
->getPassword(), aEncData
);
404 eResult
= DocPasswordVerifierResult_ABORT
;
406 eRequestMode
= PasswordRequestMode_PASSWORD_REENTER
;
413 return (eResult
== DocPasswordVerifierResult_OK
) ? aEncData
: uno::Sequence
< beans::NamedValue
>();
416 /*static*/ ::com::sun::star::uno::Sequence
< ::com::sun::star::beans::NamedValue
> DocPasswordHelper::requestAndVerifyDocPassword(
417 IDocPasswordVerifier
& rVerifier
,
418 MediaDescriptor
& rMediaDesc
,
419 DocPasswordRequestType eRequestType
,
420 const ::std::vector
< OUString
>* pDefaultPasswords
)
422 uno::Sequence
< beans::NamedValue
> aMediaEncData
= rMediaDesc
.getUnpackedValueOrDefault(
423 MediaDescriptor::PROP_ENCRYPTIONDATA(), uno::Sequence
< beans::NamedValue
>() );
424 OUString aMediaPassword
= rMediaDesc
.getUnpackedValueOrDefault(
425 MediaDescriptor::PROP_PASSWORD(), OUString() );
426 Reference
< XInteractionHandler
> xInteractHandler
= rMediaDesc
.getUnpackedValueOrDefault(
427 MediaDescriptor::PROP_INTERACTIONHANDLER(), Reference
< XInteractionHandler
>() );
428 OUString aDocumentName
= rMediaDesc
.getUnpackedValueOrDefault(
429 MediaDescriptor::PROP_URL(), OUString() );
431 bool bIsDefaultPassword
= false;
432 uno::Sequence
< beans::NamedValue
> aEncryptionData
= requestAndVerifyDocPassword(
433 rVerifier
, aMediaEncData
, aMediaPassword
, xInteractHandler
, aDocumentName
, eRequestType
, pDefaultPasswords
, &bIsDefaultPassword
);
435 rMediaDesc
.erase( MediaDescriptor::PROP_PASSWORD() );
436 rMediaDesc
.erase( MediaDescriptor::PROP_ENCRYPTIONDATA() );
438 // insert valid password into media descriptor (but not a default password)
439 if( (aEncryptionData
.getLength() > 0) && !bIsDefaultPassword
)
440 rMediaDesc
[ MediaDescriptor::PROP_ENCRYPTIONDATA() ] <<= aEncryptionData
;
442 return aEncryptionData
;
445 // ============================================================================
447 } // namespace comphelper
449 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */