1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_comphelper.hxx" 26 27 #include "comphelper/docpasswordhelper.hxx" 28 #include <com/sun/star/task/XInteractionHandler.hpp> 29 #include "comphelper/mediadescriptor.hxx" 30 31 #include <osl/time.h> 32 #include <rtl/digest.h> 33 #include <rtl/random.h> 34 35 using ::rtl::OUString; 36 using ::com::sun::star::uno::Sequence; 37 using ::com::sun::star::uno::Exception; 38 using ::com::sun::star::uno::Reference; 39 using ::com::sun::star::uno::UNO_SET_THROW; 40 using ::com::sun::star::task::PasswordRequestMode; 41 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_ENTER; 42 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_REENTER; 43 using ::com::sun::star::task::XInteractionHandler; 44 using ::com::sun::star::task::XInteractionRequest; 45 46 using namespace ::com::sun::star; 47 48 namespace comphelper { 49 50 // ============================================================================ 51 52 static uno::Sequence< sal_Int8 > GeneratePBKDF2Hash( const ::rtl::OUString& aPassword, const uno::Sequence< sal_Int8 >& aSalt, sal_Int32 nCount, sal_Int32 nHashLength ) 53 { 54 uno::Sequence< sal_Int8 > aResult; 55 56 if ( aPassword.getLength() && aSalt.getLength() && nCount && nHashLength ) 57 { 58 ::rtl::OString aBytePass = ::rtl::OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 ); 59 aResult.realloc( 16 ); 60 rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8 * > ( aResult.getArray() ), 61 aResult.getLength(), 62 reinterpret_cast < const sal_uInt8 * > ( aBytePass.getStr() ), 63 aBytePass.getLength(), 64 reinterpret_cast < const sal_uInt8 * > ( aSalt.getConstArray() ), 65 aSalt.getLength(), 66 nCount ); 67 } 68 69 return aResult; 70 } 71 72 // ============================================================================ 73 74 IDocPasswordVerifier::~IDocPasswordVerifier() 75 { 76 } 77 78 // ============================================================================ 79 uno::Sequence< beans::PropertyValue > DocPasswordHelper::GenerateNewModifyPasswordInfo( const ::rtl::OUString& aPassword ) 80 { 81 uno::Sequence< beans::PropertyValue > aResult; 82 83 uno::Sequence< sal_Int8 > aSalt = GenerateRandomByteSequence( 16 ); 84 sal_Int32 nCount = 1024; 85 86 uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash( aPassword, aSalt, nCount, 16 ); 87 if ( aNewHash.getLength() ) 88 { 89 aResult.realloc( 4 ); 90 aResult[0].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "algorithm-name" ) ); 91 aResult[0].Value <<= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PBKDF2" ) ); 92 aResult[1].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "salt" ) ); 93 aResult[1].Value <<= aSalt; 94 aResult[2].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "iteration-count" ) ); 95 aResult[2].Value <<= nCount; 96 aResult[3].Name = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hash" ) ); 97 aResult[3].Value <<= aNewHash; 98 } 99 100 return aResult; 101 } 102 103 // ============================================================================ 104 sal_Bool DocPasswordHelper::IsModifyPasswordCorrect( const ::rtl::OUString& aPassword, const uno::Sequence< beans::PropertyValue >& aInfo ) 105 { 106 sal_Bool bResult = sal_False; 107 if ( aPassword.getLength() && aInfo.getLength() ) 108 { 109 ::rtl::OUString sAlgorithm; 110 uno::Sequence< sal_Int8 > aSalt; 111 uno::Sequence< sal_Int8 > aHash; 112 sal_Int32 nCount = 0; 113 114 for ( sal_Int32 nInd = 0; nInd < aInfo.getLength(); nInd++ ) 115 { 116 if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "algorithm-name" ) ) ) ) 117 aInfo[nInd].Value >>= sAlgorithm; 118 else if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "salt" ) ) ) ) 119 aInfo[nInd].Value >>= aSalt; 120 else if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "iteration-count" ) ) ) ) 121 aInfo[nInd].Value >>= nCount; 122 else if ( aInfo[nInd].Name.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "hash" ) ) ) ) 123 aInfo[nInd].Value >>= aHash; 124 } 125 126 if ( sAlgorithm.equals( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PBKDF2" ) ) ) 127 && aSalt.getLength() && nCount > 0 && aHash.getLength() ) 128 { 129 uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash( aPassword, aSalt, nCount, aHash.getLength() ); 130 for ( sal_Int32 nInd = 0; nInd < aNewHash.getLength() && nInd < aHash.getLength() && aNewHash[nInd] == aHash[nInd]; nInd ++ ) 131 { 132 if ( nInd == aNewHash.getLength() - 1 && nInd == aHash.getLength() - 1 ) 133 bResult = sal_True; 134 } 135 } 136 } 137 138 return bResult; 139 } 140 141 // ============================================================================ 142 sal_uInt32 DocPasswordHelper::GetWordHashAsUINT32( 143 const ::rtl::OUString& aUString ) 144 { 145 static sal_uInt16 pInitialCode[] = { 146 0xE1F0, // 1 147 0x1D0F, // 2 148 0xCC9C, // 3 149 0x84C0, // 4 150 0x110C, // 5 151 0x0E10, // 6 152 0xF1CE, // 7 153 0x313E, // 8 154 0x1872, // 9 155 0xE139, // 10 156 0xD40F, // 11 157 0x84F9, // 12 158 0x280C, // 13 159 0xA96A, // 14 160 0x4EC3 // 15 161 }; 162 163 static sal_uInt16 pEncryptionMatrix[15][7] = { 164 { 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09}, // last-14 165 { 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF}, // last-13 166 { 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0}, // last-12 167 { 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40}, // last-11 168 { 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5}, // last-10 169 { 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A}, // last-9 170 { 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9}, // last-8 171 { 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0}, // last-7 172 { 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC}, // last-6 173 { 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10}, // last-5 174 { 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168}, // last-4 175 { 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C}, // last-3 176 { 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD}, // last-2 177 { 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC}, // last-1 178 { 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4} // last 179 }; 180 181 sal_uInt32 nResult = 0; 182 sal_uInt32 nLen = aUString.getLength(); 183 184 if ( nLen ) 185 { 186 if ( nLen > 15 ) 187 nLen = 15; 188 189 sal_uInt16 nHighResult = pInitialCode[nLen - 1]; 190 sal_uInt16 nLowResult = 0; 191 192 const sal_Unicode* pStr = aUString.getStr(); 193 for ( sal_uInt32 nInd = 0; nInd < nLen; nInd++ ) 194 { 195 // NO Encoding during conversion! 196 // The specification says that the low byte should be used in case it is not NULL 197 char nHighChar = (char)( pStr[nInd] >> 8 ); 198 char nLowChar = (char)( pStr[nInd] & 0xFF ); 199 char nChar = nLowChar ? nLowChar : nHighChar; 200 201 for ( int nMatrixInd = 0; nMatrixInd < 7; ++nMatrixInd ) 202 { 203 if ( ( nChar & ( 1 << nMatrixInd ) ) != 0 ) 204 nHighResult = nHighResult ^ pEncryptionMatrix[15 - nLen + nInd][nMatrixInd]; 205 } 206 207 nLowResult = ( ( ( nLowResult >> 14 ) & 0x0001 ) | ( ( nLowResult << 1 ) & 0x7FFF ) ) ^ nChar; 208 } 209 210 nLowResult = (sal_uInt16)( ( ( ( nLowResult >> 14 ) & 0x001 ) | ( ( nLowResult << 1 ) & 0x7FF ) ) ^ nLen ^ 0xCE4B ); 211 212 nResult = ( nHighResult << 16 ) | nLowResult; 213 } 214 215 return nResult; 216 } 217 218 // ============================================================================ 219 Sequence< sal_Int8 > DocPasswordHelper::GetWordHashAsSequence( 220 const ::rtl::OUString& aUString ) 221 { 222 sal_uInt32 nHash = GetWordHashAsUINT32( aUString ); 223 Sequence< sal_Int8 > aResult( 4 ); 224 aResult[0] = ( nHash >> 24 ); 225 aResult[1] = ( ( nHash >> 16 ) & 0xFF ); 226 aResult[2] = ( ( nHash >> 8 ) & 0xFF ); 227 aResult[3] = ( nHash & 0xFF ); 228 229 return aResult; 230 } 231 232 // ============================================================================ 233 sal_uInt16 DocPasswordHelper::GetXLHashAsUINT16( 234 const ::rtl::OUString& aUString, 235 rtl_TextEncoding nEnc ) 236 { 237 sal_uInt16 nResult = 0; 238 239 ::rtl::OString aString = ::rtl::OUStringToOString( aUString, nEnc ); 240 241 if ( aString.getLength() && aString.getLength() <= SAL_MAX_UINT16 ) 242 { 243 for ( sal_Int32 nInd = aString.getLength() - 1; nInd >= 0; nInd-- ) 244 { 245 nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF ); 246 nResult ^= aString.getStr()[nInd]; 247 } 248 249 nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF ); 250 nResult ^= ( 0x8000 | ( 'N' << 8 ) | 'K' ); 251 nResult ^= aString.getLength(); 252 } 253 254 return nResult; 255 } 256 257 // ============================================================================ 258 Sequence< sal_Int8 > DocPasswordHelper::GetXLHashAsSequence( 259 const ::rtl::OUString& aUString, 260 rtl_TextEncoding nEnc ) 261 { 262 sal_uInt16 nHash = GetXLHashAsUINT16( aUString, nEnc ); 263 Sequence< sal_Int8 > aResult( 2 ); 264 aResult[0] = ( nHash >> 8 ); 265 aResult[1] = ( nHash & 0xFF ); 266 267 return aResult; 268 } 269 270 // ============================================================================ 271 /*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateRandomByteSequence( sal_Int32 nLength ) 272 { 273 uno::Sequence< sal_Int8 > aResult( nLength ); 274 275 TimeValue aTime; 276 osl_getSystemTime( &aTime ); 277 rtlRandomPool aRandomPool = rtl_random_createPool (); 278 rtl_random_addBytes ( aRandomPool, &aTime, 8 ); 279 rtl_random_getBytes ( aRandomPool, aResult.getArray(), nLength ); 280 rtl_random_destroyPool ( aRandomPool ); 281 282 return aResult; 283 } 284 285 286 // ============================================================================ 287 /*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const ::rtl::OUString& aPassword, const uno::Sequence< sal_Int8 >& aDocId ) 288 { 289 uno::Sequence< sal_Int8 > aResultKey; 290 if ( aPassword.getLength() && aDocId.getLength() == 16 ) 291 { 292 sal_uInt16 pPassData[16]; 293 rtl_zeroMemory( pPassData, sizeof(pPassData) ); 294 295 sal_Int32 nPassLen = ::std::min< sal_Int32 >( aPassword.getLength(), 15 ); 296 rtl_copyMemory( pPassData, aPassword.getStr(), nPassLen * sizeof(pPassData[0]) ); 297 298 aResultKey = GenerateStd97Key( pPassData, aDocId ); 299 } 300 301 return aResultKey; 302 } 303 304 // ============================================================================ 305 /*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const uno::Sequence< sal_Int8 >& aDocId ) 306 { 307 uno::Sequence< sal_Int8 > aResultKey; 308 if ( pPassData[0] && aDocId.getLength() == 16 ) 309 { 310 sal_uInt8 pKeyData[64]; 311 rtl_zeroMemory( pKeyData, sizeof(pKeyData) ); 312 313 sal_Int32 nInd = 0; 314 315 // Fill PassData into KeyData. 316 for ( nInd = 0; nInd < 16 && pPassData[nInd]; nInd++) 317 { 318 pKeyData[2*nInd] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 0) & 0xff ); 319 pKeyData[2*nInd + 1] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 8) & 0xff ); 320 } 321 322 pKeyData[2*nInd] = 0x80; 323 pKeyData[56] = sal::static_int_cast< sal_uInt8 >( nInd << 4 ); 324 325 // Fill raw digest of KeyData into KeyData. 326 rtlDigest hDigest = rtl_digest_create ( rtl_Digest_AlgorithmMD5 ); 327 (void)rtl_digest_updateMD5 ( 328 hDigest, pKeyData, sizeof(pKeyData)); 329 (void)rtl_digest_rawMD5 ( 330 hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5); 331 332 // Update digest with KeyData and Unique. 333 for ( nInd = 0; nInd < 16; nInd++ ) 334 { 335 rtl_digest_updateMD5( hDigest, pKeyData, 5 ); 336 rtl_digest_updateMD5( hDigest, (const sal_uInt8*)aDocId.getConstArray(), aDocId.getLength() ); 337 } 338 339 // Update digest with padding. 340 pKeyData[16] = 0x80; 341 rtl_zeroMemory( pKeyData + 17, sizeof(pKeyData) - 17 ); 342 pKeyData[56] = 0x80; 343 pKeyData[57] = 0x0a; 344 345 rtl_digest_updateMD5( hDigest, &(pKeyData[16]), sizeof(pKeyData) - 16 ); 346 347 // Fill raw digest of above updates 348 aResultKey.realloc( RTL_DIGEST_LENGTH_MD5 ); 349 rtl_digest_rawMD5 ( hDigest, (sal_uInt8*)aResultKey.getArray(), aResultKey.getLength() ); 350 351 // Erase KeyData array and leave. 352 rtl_zeroMemory( pKeyData, sizeof(pKeyData) ); 353 } 354 355 return aResultKey; 356 } 357 358 // ============================================================================ 359 360 /*static*/ ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > DocPasswordHelper::requestAndVerifyDocPassword( 361 IDocPasswordVerifier& rVerifier, 362 const ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue >& rMediaEncData, 363 const OUString& rMediaPassword, 364 const Reference< XInteractionHandler >& rxInteractHandler, 365 const OUString& rDocumentName, 366 DocPasswordRequestType eRequestType, 367 const ::std::vector< OUString >* pDefaultPasswords, 368 bool* pbIsDefaultPassword ) 369 { 370 ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > aEncData; 371 DocPasswordVerifierResult eResult = DocPasswordVerifierResult_WRONG_PASSWORD; 372 373 // first, try provided default passwords 374 if( pbIsDefaultPassword ) 375 *pbIsDefaultPassword = false; 376 if( pDefaultPasswords ) 377 { 378 for( ::std::vector< OUString >::const_iterator aIt = pDefaultPasswords->begin(), aEnd = pDefaultPasswords->end(); (eResult == DocPasswordVerifierResult_WRONG_PASSWORD) && (aIt != aEnd); ++aIt ) 379 { 380 OSL_ENSURE( aIt->getLength() > 0, "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" ); 381 if( aIt->getLength() > 0 ) 382 { 383 eResult = rVerifier.verifyPassword( *aIt, aEncData ); 384 if( pbIsDefaultPassword ) 385 *pbIsDefaultPassword = eResult == DocPasswordVerifierResult_OK; 386 } 387 } 388 } 389 390 // try media encryption data (skip, if result is OK or ABORT) 391 if( eResult == DocPasswordVerifierResult_WRONG_PASSWORD ) 392 { 393 if( rMediaEncData.getLength() > 0 ) 394 { 395 eResult = rVerifier.verifyEncryptionData( rMediaEncData ); 396 if( eResult == DocPasswordVerifierResult_OK ) 397 aEncData = rMediaEncData; 398 } 399 } 400 401 // try media password (skip, if result is OK or ABORT) 402 if( eResult == DocPasswordVerifierResult_WRONG_PASSWORD ) 403 { 404 if( rMediaPassword.getLength() > 0 ) 405 eResult = rVerifier.verifyPassword( rMediaPassword, aEncData ); 406 } 407 408 // request a password (skip, if result is OK or ABORT) 409 if( (eResult == DocPasswordVerifierResult_WRONG_PASSWORD) && rxInteractHandler.is() ) try 410 { 411 PasswordRequestMode eRequestMode = PasswordRequestMode_PASSWORD_ENTER; 412 while( eResult == DocPasswordVerifierResult_WRONG_PASSWORD ) 413 { 414 DocPasswordRequest* pRequest = new DocPasswordRequest( eRequestType, eRequestMode, rDocumentName ); 415 Reference< XInteractionRequest > xRequest( pRequest ); 416 rxInteractHandler->handle( xRequest ); 417 if( pRequest->isPassword() ) 418 { 419 if( pRequest->getPassword().getLength() > 0 ) 420 eResult = rVerifier.verifyPassword( pRequest->getPassword(), aEncData ); 421 } 422 else 423 { 424 eResult = DocPasswordVerifierResult_ABORT; 425 } 426 eRequestMode = PasswordRequestMode_PASSWORD_REENTER; 427 } 428 } 429 catch( Exception& ) 430 { 431 } 432 433 return (eResult == DocPasswordVerifierResult_OK) ? aEncData : uno::Sequence< beans::NamedValue >(); 434 } 435 436 /*static*/ ::com::sun::star::uno::Sequence< ::com::sun::star::beans::NamedValue > DocPasswordHelper::requestAndVerifyDocPassword( 437 IDocPasswordVerifier& rVerifier, 438 MediaDescriptor& rMediaDesc, 439 DocPasswordRequestType eRequestType, 440 const ::std::vector< OUString >* pDefaultPasswords ) 441 { 442 uno::Sequence< beans::NamedValue > aMediaEncData = rMediaDesc.getUnpackedValueOrDefault( 443 MediaDescriptor::PROP_ENCRYPTIONDATA(), uno::Sequence< beans::NamedValue >() ); 444 OUString aMediaPassword = rMediaDesc.getUnpackedValueOrDefault( 445 MediaDescriptor::PROP_PASSWORD(), OUString() ); 446 Reference< XInteractionHandler > xInteractHandler = rMediaDesc.getUnpackedValueOrDefault( 447 MediaDescriptor::PROP_INTERACTIONHANDLER(), Reference< XInteractionHandler >() ); 448 OUString aDocumentName = rMediaDesc.getUnpackedValueOrDefault( 449 MediaDescriptor::PROP_URL(), OUString() ); 450 451 bool bIsDefaultPassword = false; 452 uno::Sequence< beans::NamedValue > aEncryptionData = requestAndVerifyDocPassword( 453 rVerifier, aMediaEncData, aMediaPassword, xInteractHandler, aDocumentName, eRequestType, pDefaultPasswords, &bIsDefaultPassword ); 454 455 rMediaDesc.erase( MediaDescriptor::PROP_PASSWORD() ); 456 rMediaDesc.erase( MediaDescriptor::PROP_ENCRYPTIONDATA() ); 457 458 // insert valid password into media descriptor (but not a default password) 459 if( (aEncryptionData.getLength() > 0) && !bIsDefaultPassword ) 460 rMediaDesc[ MediaDescriptor::PROP_ENCRYPTIONDATA() ] <<= aEncryptionData; 461 462 return aEncryptionData; 463 } 464 465 // ============================================================================ 466 467 } // namespace comphelper 468 469