LibreOffice Module comphelper (master)  1
docpasswordhelper.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_gpgme.h>
21 
22 #include <algorithm>
23 #include <string_view>
24 
28 #include <comphelper/hash.hxx>
29 #include <comphelper/base64.hxx>
30 #include <comphelper/sequence.hxx>
31 #include <com/sun/star/beans/NamedValue.hpp>
32 #include <com/sun/star/beans/PropertyValue.hpp>
33 #include <com/sun/star/task/XInteractionHandler.hpp>
34 
35 #include <osl/diagnose.h>
36 #include <sal/log.hxx>
37 #include <rtl/digest.h>
38 #include <rtl/random.h>
39 #include <string.h>
40 
41 #if HAVE_FEATURE_GPGME
42 # include <context.h>
43 # include <data.h>
44 # include <decryptionresult.h>
45 #endif
46 
47 using ::com::sun::star::uno::Sequence;
48 using ::com::sun::star::uno::Exception;
49 using ::com::sun::star::uno::Reference;
50 using ::com::sun::star::task::PasswordRequestMode;
51 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_ENTER;
52 using ::com::sun::star::task::PasswordRequestMode_PASSWORD_REENTER;
53 using ::com::sun::star::task::XInteractionHandler;
54 using ::com::sun::star::task::XInteractionRequest;
55 
56 using namespace ::com::sun::star;
57 
58 namespace comphelper {
59 
60 
61 static uno::Sequence< sal_Int8 > GeneratePBKDF2Hash( std::u16string_view aPassword, const uno::Sequence< sal_Int8 >& aSalt, sal_Int32 nCount, sal_Int32 nHashLength )
62 {
63  uno::Sequence< sal_Int8 > aResult;
64 
65  if ( !aPassword.empty() && aSalt.hasElements() && nCount && nHashLength )
66  {
67  OString aBytePass = OUStringToOString( aPassword, RTL_TEXTENCODING_UTF8 );
68  // FIXME this is subject to the SHA1-bug tdf#114939 - see also
69  // RequestPassword() in filedlghelper.cxx
70  aResult.realloc( 16 );
71  rtl_digest_PBKDF2( reinterpret_cast < sal_uInt8 * > ( aResult.getArray() ),
72  aResult.getLength(),
73  reinterpret_cast < const sal_uInt8 * > ( aBytePass.getStr() ),
74  aBytePass.getLength(),
75  reinterpret_cast < const sal_uInt8 * > ( aSalt.getConstArray() ),
76  aSalt.getLength(),
77  nCount );
78  }
79 
80  return aResult;
81 }
82 
83 
85 {
86 }
87 
88 
89 uno::Sequence< beans::PropertyValue > DocPasswordHelper::GenerateNewModifyPasswordInfo( std::u16string_view aPassword )
90 {
91  uno::Sequence< beans::PropertyValue > aResult;
92 
93  uno::Sequence< sal_Int8 > aSalt = GenerateRandomByteSequence( 16 );
94  sal_Int32 const nPBKDF2IterationCount = 100000;
95 
96  uno::Sequence< sal_Int8 > aNewHash = GeneratePBKDF2Hash(aPassword, aSalt, nPBKDF2IterationCount, 16);
97  if ( aNewHash.hasElements() )
98  {
99  aResult = { comphelper::makePropertyValue("algorithm-name", OUString( "PBKDF2" )),
100  comphelper::makePropertyValue("salt", aSalt),
101  comphelper::makePropertyValue("iteration-count", nPBKDF2IterationCount),
102  comphelper::makePropertyValue("hash", aNewHash) };
103  }
104 
105  return aResult;
106 }
107 
108 
109 uno::Sequence<beans::PropertyValue>
111 {
112  uno::Sequence<beans::PropertyValue> aResult;
113 
114  if (!aPassword.empty())
115  {
116  uno::Sequence<sal_Int8> aSalt = GenerateRandomByteSequence(16);
117  OUStringBuffer aBuffer(22);
118  comphelper::Base64::encode(aBuffer, aSalt);
119  OUString sSalt = aBuffer.makeStringAndClear();
120 
121  sal_Int32 const nIterationCount = 100000;
122  OUString sAlgorithm("SHA-512");
123 
124  const OUString sHash(GetOoxHashAsBase64(OUString(aPassword), sSalt, nIterationCount,
126 
127  if (!sHash.isEmpty())
128  {
129  aResult = { comphelper::makePropertyValue("algorithm-name", sAlgorithm),
130  comphelper::makePropertyValue("salt", sSalt),
131  comphelper::makePropertyValue("iteration-count", nIterationCount),
132  comphelper::makePropertyValue("hash", sHash) };
133  }
134  }
135 
136  return aResult;
137 }
138 
139 
140 uno::Sequence< beans::PropertyValue > DocPasswordHelper::ConvertPasswordInfo( const uno::Sequence< beans::PropertyValue >& aInfo )
141 {
142  uno::Sequence< beans::PropertyValue > aResult;
143  OUString sAlgorithm, sHash, sSalt, sCount;
144  sal_Int32 nAlgorithm = 0;
145 
146  for ( const auto & prop : aInfo )
147  {
148  if ( prop.Name == "cryptAlgorithmSid" )
149  {
150  prop.Value >>= sAlgorithm;
151  nAlgorithm = sAlgorithm.toInt32();
152  }
153  else if ( prop.Name == "salt" )
154  prop.Value >>= sSalt;
155  else if ( prop.Name == "cryptSpinCount" )
156  prop.Value >>= sCount;
157  else if ( prop.Name == "hash" )
158  prop.Value >>= sHash;
159  }
160 
161  if (nAlgorithm == 1)
162  sAlgorithm = "MD2";
163  else if (nAlgorithm == 2)
164  sAlgorithm = "MD4";
165  else if (nAlgorithm == 3)
166  sAlgorithm = "MD5";
167  else if (nAlgorithm == 4)
168  sAlgorithm = "SHA-1";
169  else if (nAlgorithm == 5)
170  sAlgorithm = "MAC";
171  else if (nAlgorithm == 6)
172  sAlgorithm = "RIPEMD";
173  else if (nAlgorithm == 7)
174  sAlgorithm = "RIPEMD-160";
175  else if (nAlgorithm == 9)
176  sAlgorithm = "HMAC";
177  else if (nAlgorithm == 12)
178  sAlgorithm = "SHA-256";
179  else if (nAlgorithm == 13)
180  sAlgorithm = "SHA-384";
181  else if (nAlgorithm == 14)
182  sAlgorithm = "SHA-512";
183 
184  if ( !sCount.isEmpty() )
185  {
186  sal_Int32 nCount = sCount.toInt32();
187  aResult = { comphelper::makePropertyValue("algorithm-name", sAlgorithm),
188  comphelper::makePropertyValue("salt", sSalt),
189  comphelper::makePropertyValue("iteration-count", nCount),
190  comphelper::makePropertyValue("hash", sHash) };
191  }
192 
193  return aResult;
194 }
195 
196 
197 bool DocPasswordHelper::IsModifyPasswordCorrect( std::u16string_view aPassword, const uno::Sequence< beans::PropertyValue >& aInfo )
198 {
199  bool bResult = false;
200  if ( !aPassword.empty() && aInfo.hasElements() )
201  {
202  OUString sAlgorithm;
203  uno::Any aSalt, aHash;
204  sal_Int32 nCount = 0;
205 
206  for ( const auto & prop : aInfo )
207  {
208  if ( prop.Name == "algorithm-name" )
209  prop.Value >>= sAlgorithm;
210  else if ( prop.Name == "salt" )
211  aSalt = prop.Value;
212  else if ( prop.Name == "iteration-count" )
213  prop.Value >>= nCount;
214  else if ( prop.Name == "hash" )
215  aHash = prop.Value;
216  }
217 
218  if ( sAlgorithm == "PBKDF2" )
219  {
220  uno::Sequence<sal_Int8> aIntSalt, aIntHash;
221  aSalt >>= aIntSalt;
222  aHash >>= aIntHash;
223  if (aIntSalt.hasElements() && nCount > 0 && aIntHash.hasElements())
224  {
225  uno::Sequence<sal_Int8> aNewHash
226  = GeneratePBKDF2Hash(aPassword, aIntSalt, nCount, aIntHash.getLength());
227  for (sal_Int32 nInd = 0; nInd < aNewHash.getLength() && nInd < aIntHash.getLength()
228  && aNewHash[nInd] == aIntHash[nInd];
229  nInd++)
230  {
231  if (nInd == aNewHash.getLength() - 1 && nInd == aIntHash.getLength() - 1)
232  bResult = true;
233  }
234  }
235  }
236  else if (nCount > 0)
237  {
238  OUString sSalt, sHash;
239  aSalt >>= sSalt;
240  aHash >>= sHash;
241  if (!sSalt.isEmpty() && !sHash.isEmpty())
242  {
243  const OUString aNewHash(GetOoxHashAsBase64(OUString(aPassword), sSalt, nCount,
245  sAlgorithm));
246  if (!aNewHash.isEmpty())
247  bResult = aNewHash == sHash;
248  }
249  }
250  }
251 
252  return bResult;
253 }
254 
255 
257  std::u16string_view aUString )
258 {
259  static const sal_uInt16 pInitialCode[] = {
260  0xE1F0, // 1
261  0x1D0F, // 2
262  0xCC9C, // 3
263  0x84C0, // 4
264  0x110C, // 5
265  0x0E10, // 6
266  0xF1CE, // 7
267  0x313E, // 8
268  0x1872, // 9
269  0xE139, // 10
270  0xD40F, // 11
271  0x84F9, // 12
272  0x280C, // 13
273  0xA96A, // 14
274  0x4EC3 // 15
275  };
276 
277  static const sal_uInt16 pEncryptionMatrix[15][7] = {
278  { 0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09}, // last-14
279  { 0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF}, // last-13
280  { 0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0}, // last-12
281  { 0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40}, // last-11
282  { 0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5}, // last-10
283  { 0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A}, // last-9
284  { 0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9}, // last-8
285  { 0x47D3, 0x8FA6, 0x8FA6, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0}, // last-7
286  { 0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC}, // last-6
287  { 0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10}, // last-5
288  { 0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168}, // last-4
289  { 0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C}, // last-3
290  { 0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD}, // last-2
291  { 0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC}, // last-1
292  { 0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4} // last
293  };
294 
295  sal_uInt32 nResult = 0;
296  size_t nLen = aUString.size();
297 
298  if ( nLen )
299  {
300  if ( nLen > 15 )
301  nLen = 15;
302 
303  sal_uInt16 nHighResult = pInitialCode[nLen - 1];
304  sal_uInt16 nLowResult = 0;
305 
306  for ( size_t nInd = 0; nInd < nLen; nInd++ )
307  {
308  // NO Encoding during conversion!
309  // The specification says that the low byte should be used in case it is not NULL
310  char nHighChar = static_cast<char>( aUString[nInd] >> 8 );
311  char nLowChar = static_cast<char>( aUString[nInd] & 0xFF );
312  char nChar = nLowChar ? nLowChar : nHighChar;
313 
314  for ( int nMatrixInd = 0; nMatrixInd < 7; ++nMatrixInd )
315  {
316  if ( ( nChar & ( 1 << nMatrixInd ) ) != 0 )
317  nHighResult = nHighResult ^ pEncryptionMatrix[15 - nLen + nInd][nMatrixInd];
318  }
319 
320  nLowResult = ( ( ( nLowResult >> 14 ) & 0x0001 ) | ( ( nLowResult << 1 ) & 0x7FFF ) ) ^ nChar;
321  }
322 
323  nLowResult = static_cast<sal_uInt16>( ( ( ( nLowResult >> 14 ) & 0x001 ) | ( ( nLowResult << 1 ) & 0x7FF ) ) ^ nLen ^ 0xCE4B );
324 
325  nResult = ( nHighResult << 16 ) | nLowResult;
326  }
327 
328  return nResult;
329 }
330 
331 
333  std::u16string_view aUString,
334  rtl_TextEncoding nEnc )
335 {
336  sal_uInt16 nResult = 0;
337 
338  OString aString = OUStringToOString( aUString, nEnc );
339 
340  if ( !aString.isEmpty() && aString.getLength() <= SAL_MAX_UINT16 )
341  {
342  for ( sal_Int32 nInd = aString.getLength() - 1; nInd >= 0; nInd-- )
343  {
344  nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF );
345  nResult ^= aString[nInd];
346  }
347 
348  nResult = ( ( nResult >> 14 ) & 0x01 ) | ( ( nResult << 1 ) & 0x7FFF );
349  nResult ^= ( 0x8000 | ( 'N' << 8 ) | 'K' );
350  nResult ^= aString.getLength();
351  }
352 
353  return nResult;
354 }
355 
356 
358  std::u16string_view aUString )
359 {
360  sal_uInt16 nHash = GetXLHashAsUINT16( aUString );
361  return {sal_Int8(nHash >> 8), sal_Int8(nHash & 0xFF)};
362 }
363 
364 
365 std::vector<unsigned char> DocPasswordHelper::GetOoxHashAsVector(
366  const OUString& rPassword,
367  const std::vector<unsigned char>& rSaltValue,
368  sal_uInt32 nSpinCount,
369  comphelper::Hash::IterCount eIterCount,
370  std::u16string_view rAlgorithmName)
371 {
373  if (rAlgorithmName == u"SHA-512" || rAlgorithmName == u"SHA512")
375  else if (rAlgorithmName == u"SHA-256" || rAlgorithmName == u"SHA256")
377  else if (rAlgorithmName == u"SHA-1" || rAlgorithmName == u"SHA1") // "SHA1" might be in the wild
379  else if (rAlgorithmName == u"MD5")
381  else
382  return std::vector<unsigned char>();
383 
384  return comphelper::Hash::calculateHash( rPassword, rSaltValue, nSpinCount, eIterCount, eType);
385 }
386 
387 
388 css::uno::Sequence<sal_Int8> DocPasswordHelper::GetOoxHashAsSequence(
389  const OUString& rPassword,
390  std::u16string_view rSaltValue,
391  sal_uInt32 nSpinCount,
392  comphelper::Hash::IterCount eIterCount,
393  std::u16string_view rAlgorithmName)
394 {
395  std::vector<unsigned char> aSaltVec;
396  if (!rSaltValue.empty())
397  {
398  css::uno::Sequence<sal_Int8> aSaltSeq;
399  comphelper::Base64::decode( aSaltSeq, rSaltValue);
400  aSaltVec = comphelper::sequenceToContainer<std::vector<unsigned char>>( aSaltSeq);
401  }
402 
403  std::vector<unsigned char> hash( GetOoxHashAsVector( rPassword, aSaltVec, nSpinCount, eIterCount, rAlgorithmName));
404 
405  return comphelper::containerToSequence<sal_Int8>( hash);
406 }
407 
409  const OUString& rPassword,
410  std::u16string_view rSaltValue,
411  sal_uInt32 nSpinCount,
412  comphelper::Hash::IterCount eIterCount,
413  std::u16string_view rAlgorithmName)
414 {
415  css::uno::Sequence<sal_Int8> aSeq( GetOoxHashAsSequence( rPassword, rSaltValue, nSpinCount,
416  eIterCount, rAlgorithmName));
417 
418  OUStringBuffer aBuf((aSeq.getLength()+2)/3*4);
419  comphelper::Base64::encode( aBuf, aSeq);
420  return aBuf.makeStringAndClear();
421 }
422 
423 
424 /*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateRandomByteSequence( sal_Int32 nLength )
425 {
426  uno::Sequence< sal_Int8 > aResult( nLength );
427 
428  rtlRandomPool aRandomPool = rtl_random_createPool ();
429  rtl_random_getBytes ( aRandomPool, aResult.getArray(), nLength );
430  rtl_random_destroyPool ( aRandomPool );
431 
432  return aResult;
433 }
434 
435 
436 /*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( std::u16string_view aPassword, const uno::Sequence< sal_Int8 >& aDocId )
437 {
438  uno::Sequence< sal_Int8 > aResultKey;
439  if ( !aPassword.empty() && aDocId.getLength() == 16 )
440  {
441  sal_uInt16 pPassData[16] = {};
442 
443  sal_Int32 nPassLen = std::min< sal_Int32 >( aPassword.size(), 15 );
444  memcpy( pPassData, aPassword.data(), nPassLen * sizeof(pPassData[0]) );
445 
446  aResultKey = GenerateStd97Key( pPassData, aDocId );
447  }
448 
449  return aResultKey;
450 }
451 
452 
453 /*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const uno::Sequence< sal_Int8 >& aDocId )
454 {
455  uno::Sequence< sal_Int8 > aResultKey;
456 
457  if ( aDocId.getLength() == 16 )
458  aResultKey = GenerateStd97Key(pPassData, reinterpret_cast<const sal_uInt8*>(aDocId.getConstArray()));
459 
460  return aResultKey;
461 }
462 
463 
464 /*static*/ uno::Sequence< sal_Int8 > DocPasswordHelper::GenerateStd97Key( const sal_uInt16 pPassData[16], const sal_uInt8 pDocId[16] )
465 {
466  uno::Sequence< sal_Int8 > aResultKey;
467  if ( pPassData[0] )
468  {
469  sal_uInt8 pKeyData[64] = {};
470 
471  sal_Int32 nInd = 0;
472 
473  // Fill PassData into KeyData.
474  for ( nInd = 0; nInd < 16 && pPassData[nInd]; nInd++)
475  {
476  pKeyData[2*nInd] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 0) & 0xff );
477  pKeyData[2*nInd + 1] = sal::static_int_cast< sal_uInt8 >( (pPassData[nInd] >> 8) & 0xff );
478  }
479 
480  pKeyData[2*nInd] = 0x80;
481  pKeyData[56] = sal::static_int_cast< sal_uInt8 >( nInd << 4 );
482 
483  // Fill raw digest of KeyData into KeyData.
484  rtlDigest hDigest = rtl_digest_create ( rtl_Digest_AlgorithmMD5 );
485  (void)rtl_digest_updateMD5 (
486  hDigest, pKeyData, sizeof(pKeyData));
487  (void)rtl_digest_rawMD5 (
488  hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5);
489 
490  // Update digest with KeyData and Unique.
491  for ( nInd = 0; nInd < 16; nInd++ )
492  {
493  rtl_digest_updateMD5( hDigest, pKeyData, 5 );
494  rtl_digest_updateMD5( hDigest, pDocId, 16 );
495  }
496 
497  // Update digest with padding.
498  pKeyData[16] = 0x80;
499  memset( pKeyData + 17, 0, sizeof(pKeyData) - 17 );
500  pKeyData[56] = 0x80;
501  pKeyData[57] = 0x0a;
502 
503  rtl_digest_updateMD5( hDigest, &(pKeyData[16]), sizeof(pKeyData) - 16 );
504 
505  // Fill raw digest of above updates
506  aResultKey.realloc( RTL_DIGEST_LENGTH_MD5 );
507  rtl_digest_rawMD5 ( hDigest, reinterpret_cast<sal_uInt8*>(aResultKey.getArray()), aResultKey.getLength() );
508 
509  // Erase KeyData array and leave.
510  rtl_secureZeroMemory (pKeyData, sizeof(pKeyData));
511 
512  rtl_digest_destroy(hDigest);
513  }
514 
515  return aResultKey;
516 }
517 
518 
519 /*static*/ css::uno::Sequence< css::beans::NamedValue > DocPasswordHelper::requestAndVerifyDocPassword(
520  IDocPasswordVerifier& rVerifier,
521  const css::uno::Sequence< css::beans::NamedValue >& rMediaEncData,
522  const OUString& rMediaPassword,
523  const Reference< XInteractionHandler >& rxInteractHandler,
524  const OUString& rDocumentUrl,
525  DocPasswordRequestType eRequestType,
526  const std::vector< OUString >* pDefaultPasswords,
527  bool* pbIsDefaultPassword )
528 {
529  css::uno::Sequence< css::beans::NamedValue > aEncData;
530  OUString aPassword;
532 
533  sal_Int32 nMediaEncDataCount = rMediaEncData.getLength();
534 
535  // tdf#93389: if the document is being restored from autorecovery, we need to add encryption
536  // data also for real document type.
537  // TODO: get real filter name here (from CheckPasswd_Impl), to only add necessary data
538  bool bForSalvage = false;
539  if (nMediaEncDataCount)
540  {
541  for (auto& val : rMediaEncData)
542  {
543  if (val.Name == "ForSalvage")
544  {
545  --nMediaEncDataCount; // don't consider this element below
546  val.Value >>= bForSalvage;
547  break;
548  }
549  }
550  }
551 
552  // first, try provided default passwords
553  if( pbIsDefaultPassword )
554  *pbIsDefaultPassword = false;
555  if( pDefaultPasswords )
556  {
557  for( const auto& rPassword : *pDefaultPasswords )
558  {
559  OSL_ENSURE( !rPassword.isEmpty(), "DocPasswordHelper::requestAndVerifyDocPassword - unexpected empty default password" );
560  if( !rPassword.isEmpty() )
561  {
562  eResult = rVerifier.verifyPassword( rPassword, aEncData );
563  if (eResult == DocPasswordVerifierResult::OK)
564  {
565  aPassword = rPassword;
566  if (pbIsDefaultPassword)
567  *pbIsDefaultPassword = true;
568  }
570  break;
571  }
572  }
573  }
574 
575  // try media encryption data (skip, if result is OK or ABORT)
577  {
578  if (nMediaEncDataCount)
579  {
580  eResult = rVerifier.verifyEncryptionData( rMediaEncData );
581  if( eResult == DocPasswordVerifierResult::OK )
582  aEncData = rMediaEncData;
583  }
584  }
585 
586  // try media password (skip, if result is OK or ABORT)
588  {
589  if( !rMediaPassword.isEmpty() )
590  {
591  eResult = rVerifier.verifyPassword( rMediaPassword, aEncData );
592  if (eResult == DocPasswordVerifierResult::OK)
593  aPassword = rMediaPassword;
594  }
595  }
596 
597  // request a password (skip, if result is OK or ABORT)
598  if( (eResult == DocPasswordVerifierResult::WrongPassword) && rxInteractHandler.is() ) try
599  {
600  PasswordRequestMode eRequestMode = PasswordRequestMode_PASSWORD_ENTER;
601  while( eResult == DocPasswordVerifierResult::WrongPassword )
602  {
603  rtl::Reference<DocPasswordRequest> pRequest = new DocPasswordRequest( eRequestType, eRequestMode, rDocumentUrl );
604  rxInteractHandler->handle( pRequest );
605  if( pRequest->isPassword() )
606  {
607  if( !pRequest->getPassword().isEmpty() )
608  eResult = rVerifier.verifyPassword( pRequest->getPassword(), aEncData );
609  if (eResult == DocPasswordVerifierResult::OK)
610  aPassword = pRequest->getPassword();
611  }
612  else
613  {
615  }
616  eRequestMode = PasswordRequestMode_PASSWORD_REENTER;
617  }
618  }
619  catch( Exception& )
620  {
621  }
622 
623  if (eResult == DocPasswordVerifierResult::OK && !aPassword.isEmpty())
624  {
625  if (std::find_if(std::cbegin(aEncData), std::cend(aEncData),
626  [](const css::beans::NamedValue& val) {
627  return val.Name == PACKAGE_ENCRYPTIONDATA_SHA256UTF8;
628  })
629  == std::cend(aEncData))
630  {
631  // tdf#118639: We need ODF encryption data for autorecovery, where password
632  // will already be unavailable, so generate and append it here
633  aEncData = comphelper::concatSequences(
634  aEncData, OStorageHelper::CreatePackageEncryptionData(aPassword));
635  }
636 
637  if (bForSalvage)
638  {
639  // TODO: add individual methods for different target filter, and only call what's needed
640 
641  // 1. Prepare binary MS formats encryption data
642  auto aUniqueID = GenerateRandomByteSequence(16);
643  auto aEnc97Key = GenerateStd97Key(aPassword, aUniqueID);
644  // 2. Add MS binary and OOXML encryption data to result
645  aEncData = comphelper::concatSequences(
646  aEncData, std::initializer_list<beans::NamedValue>{
647  { "STD97EncryptionKey", css::uno::Any(aEnc97Key) },
648  { "STD97UniqueID", css::uno::Any(aUniqueID) },
649  { "OOXPassword", css::uno::Any(aPassword) },
650  });
651  }
652  }
653 
654  return (eResult == DocPasswordVerifierResult::OK) ? aEncData : uno::Sequence< beans::NamedValue >();
655 }
656 
657 /*static*/ uno::Sequence< css::beans::NamedValue >
659  const uno::Sequence< uno::Sequence< beans::NamedValue > >& rGpgProperties )
660 {
661 #if HAVE_FEATURE_GPGME
662  if ( !rGpgProperties.hasElements() )
663  return uno::Sequence< beans::NamedValue >();
664 
665  uno::Sequence< beans::NamedValue > aEncryptionData;
666  std::unique_ptr<GpgME::Context> ctx;
667  GpgME::initializeLibrary();
668  GpgME::Error err = GpgME::checkEngine(GpgME::OpenPGP);
669  if (err)
670  throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
671 
672  ctx.reset( GpgME::Context::createForProtocol(GpgME::OpenPGP) );
673  if (ctx == nullptr)
674  throw uno::RuntimeException("The GpgME library failed to initialize for the OpenPGP protocol.");
675  ctx->setArmor(false);
676 
677  const uno::Sequence < beans::NamedValue > *pSequence = rGpgProperties.getConstArray();
678  const sal_Int32 nLength = rGpgProperties.getLength();
679  for ( sal_Int32 i = 0; i < nLength ; i++, pSequence++ )
680  {
681  const beans::NamedValue *pValues = pSequence->getConstArray();
682  if ( pSequence->getLength() == 3 )
683  {
684  // take CipherValue and try to decrypt that - stop after
685  // the first successful decryption
686 
687  // ctx is setup now, let's decrypt the lot!
688  uno::Sequence < sal_Int8 > aVector;
689  pValues[2].Value >>= aVector;
690 
691  GpgME::Data cipher(
692  reinterpret_cast<const char*>(aVector.getConstArray()),
693  size_t(aVector.getLength()), false);
694  GpgME::Data plain;
695 
696  GpgME::DecryptionResult crypt_res = ctx->decrypt(
697  cipher, plain);
698 
699  // NO_SECKEY -> skip
700  // BAD_PASSPHRASE -> retry?
701 
702  off_t result = plain.seek(0,SEEK_SET);
703  (void) result;
704  assert(result == 0);
705  int len=0, curr=0; char buf;
706  while( (curr=plain.read(&buf, 1)) )
707  len += curr;
708 
709  if(crypt_res.error() || !len)
710  continue; // can't use this key, take next one
711 
712  uno::Sequence < sal_Int8 > aKeyValue(len);
713  result = plain.seek(0,SEEK_SET);
714  assert(result == 0);
715  if( plain.read(aKeyValue.getArray(), len) != len )
716  throw uno::RuntimeException("The GpgME library failed to read the encrypted value.");
717 
718  SAL_INFO("comphelper.crypto", "Extracted gpg session key of length: " << len);
719 
720  aEncryptionData = { { PACKAGE_ENCRYPTIONDATA_SHA256UTF8, uno::Any(aKeyValue) } };
721  break;
722  }
723  }
724 
725  if ( aEncryptionData.hasElements() )
726  {
727  uno::Sequence< beans::NamedValue > aContainer{
728  { "GpgInfos", uno::Any(rGpgProperties) }, { "EncryptionKey", uno::Any(aEncryptionData) }
729  };
730 
731  return aContainer;
732  }
733 #else
734  (void)rGpgProperties;
735 #endif
736  return uno::Sequence< beans::NamedValue >();
737 }
738 
739 } // namespace comphelper
740 
741 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
static std::vector< unsigned char > GetOoxHashAsVector(const OUString &rPassword, const std::vector< unsigned char > &rSaltValue, sal_uInt32 nSpinCount, comphelper::Hash::IterCount eIterCount, std::u16string_view rAlgorithmName)
Convenience function to calculate a salted hash with iterations as specified in https://msdn.microsoft.com/en-us/library/dd920692 for the OOXML sheetProtection and fileSharing elements, or https://msdn.microsoft.com/en-us/library/dd924776 and https://msdn.microsoft.com/en-us/library/dd925430 for Standard and Agile Encryption.
Implements the task.XInteractionRequest interface for requesting a password string for a document...
signed char sal_Int8
virtual DocPasswordVerifierResult verifyEncryptionData(const css::uno::Sequence< css::beans::NamedValue > &o_rEncryptionData)=0
Will be called every time an encryption data needs to be verified.
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
Creates a beans::PropertyValue easily, i.e.
static void decode(css::uno::Sequence< sal_Int8 > &aPass, std::u16string_view sBuffer)
Definition: base64.cxx:144
static css::uno::Sequence< css::beans::PropertyValue > GenerateNewModifyPasswordInfoOOXML(std::u16string_view aPassword)
aBuf
static bool IsModifyPasswordCorrect(std::u16string_view aPassword, const css::uno::Sequence< css::beans::PropertyValue > &aInfo)
This helper function allows to check whether the "Password to modify" provided by user is the correct...
char const sHash[]
static css::uno::Sequence< sal_Int8 > GenerateStd97Key(std::u16string_view aPassword, const css::uno::Sequence< sal_Int8 > &aDocId)
This helper function generates a byte sequence representing the key digest value used by MSCodec_Std9...
#define SAL_MAX_UINT16
int nCount
Iteration count prepended to hash iterations.
static css::uno::Sequence< css::beans::NamedValue > CreatePackageEncryptionData(std::u16string_view aPassword)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
static void encode(OUStringBuffer &aStrBuffer, const css::uno::Sequence< sal_Int8 > &aPass)
encodes the given byte sequence into Base64
constexpr OUStringLiteral PACKAGE_ENCRYPTIONDATA_SHA256UTF8
DocumentType eType
err
int i
static css::uno::Sequence< css::beans::NamedValue > decryptGpgSession(const css::uno::Sequence< css::uno::Sequence< css::beans::NamedValue > > &rGpgProperties)
static sal_uInt16 GetXLHashAsUINT16(std::u16string_view aString, rtl_TextEncoding nEnc=RTL_TEXTENCODING_UTF8)
This helper function generates the hash code based on the algorithm specified by MS for "Password to ...
float u
Object Value
static css::uno::Sequence< sal_Int8 > GetXLHashAsSequence(std::u16string_view aString)
This helper function generates the hash code based on the algorithm specified by MS for "Password to ...
tuple ctx
css::uno::Sequence< T > concatSequences(const css::uno::Sequence< T > &rS1, const Ss &...rSn)
concat several sequences
Definition: sequence.hxx:49
DocPasswordRequestType
Selects which UNO document password request type to use.
static css::uno::Sequence< css::beans::PropertyValue > GenerateNewModifyPasswordInfo(std::u16string_view aPassword)
This helper function generates the information related to "Password to modify" provided by user...
static css::uno::Sequence< sal_Int8 > GetOoxHashAsSequence(const OUString &rPassword, std::u16string_view rSaltValue, sal_uInt32 nSpinCount, comphelper::Hash::IterCount eIterCount, std::u16string_view rAlgorithmName)
Convenience function to calculate a salted hash with iterations as specified in https://msdn.microsoft.com/en-us/library/dd920692 for the OOXML sheetProtection and fileSharing elements, or https://msdn.microsoft.com/en-us/library/dd924776 and https://msdn.microsoft.com/en-us/library/dd925430 for Standard and Agile Encryption.
static css::uno::Sequence< sal_Int8 > GenerateRandomByteSequence(sal_Int32 nLength)
This helper function generates a random sequence of bytes of requested length.
static OUString GetOoxHashAsBase64(const OUString &rPassword, std::u16string_view rSaltValue, sal_uInt32 nSpinCount, comphelper::Hash::IterCount eIterCount, std::u16string_view rAlgorithmName)
Convenience function to calculate a salted hash with iterations as specified in https://msdn.microsoft.com/en-us/library/dd920692 for the OOXML sheetProtection and fileSharing elements, or https://msdn.microsoft.com/en-us/library/dd924776 and https://msdn.microsoft.com/en-us/library/dd925430 for Standard and Agile Encryption.
const PropertyValue * pValues
void * rtlRandomPool
std::unique_ptr< char[]> aBuffer
unsigned char sal_uInt8
Base class for a password verifier used by the DocPasswordHelper class below.
static css::uno::Sequence< css::beans::NamedValue > requestAndVerifyDocPassword(IDocPasswordVerifier &rVerifier, const css::uno::Sequence< css::beans::NamedValue > &rMediaEncData, const OUString &rMediaPassword, const css::uno::Reference< css::task::XInteractionHandler > &rxInteractHandler, const OUString &rDocumentUrl, DocPasswordRequestType eRequestType, const ::std::vector< OUString > *pDefaultPasswords=nullptr, bool *pbIsDefaultPassword=nullptr)
This helper function tries to request and verify a password to load a protected document.
#define SAL_INFO(area, stream)
static sal_uInt32 GetWordHashAsUINT32(std::u16string_view aString)
This helper function generates the hash code based on the algorithm specified by MS for "Password to ...
Sequence< sal_Int8 > aSeq
static css::uno::Sequence< css::beans::PropertyValue > ConvertPasswordInfo(const css::uno::Sequence< css::beans::PropertyValue > &aInfo)
This helper function converts a grab-bagged password, e.g.
static std::vector< unsigned char > calculateHash(const unsigned char *pInput, size_t length, HashType eType)
Definition: hash.cxx:154
Any result
static uno::Sequence< sal_Int8 > GeneratePBKDF2Hash(std::u16string_view aPassword, const uno::Sequence< sal_Int8 > &aSalt, sal_Int32 nCount, sal_Int32 nHashLength)
sal_Int32 nLength
virtual DocPasswordVerifierResult verifyPassword(const OUString &rPassword, css::uno::Sequence< css::beans::NamedValue > &o_rEncryptionData)=0
Will be called every time a password needs to be verified.
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo