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 .
20 #include "filter/msfilter/mscodec.hxx"
22 #include <osl/diagnose.h>
25 #include <tools/solar.h>
27 #include <comphelper/sequenceashashmap.hxx>
28 #include <comphelper/docpasswordhelper.hxx>
30 #define DEBUG_MSO_ENCRYPTION_STD97 0
32 #if DEBUG_MSO_ENCRYPTION_STD97
36 using namespace ::com::sun::star
;
40 // ============================================================================
44 /** Rotates rnValue left by nBits bits. */
45 template< typename Type
>
46 inline void lclRotateLeft( Type
& rnValue
, int nBits
)
50 sal::static_int_cast
< unsigned int >(nBits
) < sizeof( Type
) * 8 );
51 rnValue
= static_cast< Type
>( (rnValue
<< nBits
) | (rnValue
>> (sizeof( Type
) * 8 - nBits
)) );
54 /** Rotates the lower nWidth bits of rnValue left by nBits bits. */
55 template< typename Type
>
56 inline void lclRotateLeft( Type
& rnValue
, sal_uInt8 nBits
, sal_uInt8 nWidth
)
58 OSL_ASSERT( (nBits
< nWidth
) && (nWidth
< sizeof( Type
) * 8) );
59 Type nMask
= static_cast< Type
>( (1UL << nWidth
) - 1 );
60 rnValue
= static_cast< Type
>(
61 ((rnValue
<< nBits
) | ((rnValue
& nMask
) >> (nWidth
- nBits
))) & nMask
);
64 sal_Size
lclGetLen( const sal_uInt8
* pnPassData
, sal_Size nBufferSize
)
67 while( (nLen
< nBufferSize
) && pnPassData
[ nLen
] ) ++nLen
;
71 sal_uInt16
lclGetKey( const sal_uInt8
* pnPassData
, sal_Size nBufferSize
)
73 sal_Size nLen
= lclGetLen( pnPassData
, nBufferSize
);
77 sal_uInt16 nKeyBase
= 0x8000;
78 sal_uInt16 nKeyEnd
= 0xFFFF;
79 const sal_uInt8
* pnChar
= pnPassData
+ nLen
- 1;
80 for( sal_Size nIndex
= 0; nIndex
< nLen
; ++nIndex
, --pnChar
)
82 sal_uInt8 cChar
= *pnChar
& 0x7F;
83 for( sal_uInt8 nBit
= 0; nBit
< 8; ++nBit
)
85 lclRotateLeft( nKeyBase
, 1 );
86 if( nKeyBase
& 1 ) nKeyBase
^= 0x1020;
87 if( cChar
& 1 ) nKey
^= nKeyBase
;
89 lclRotateLeft( nKeyEnd
, 1 );
90 if( nKeyEnd
& 1 ) nKeyEnd
^= 0x1020;
93 return nKey
^ nKeyEnd
;
96 sal_uInt16
lclGetHash( const sal_uInt8
* pnPassData
, sal_Size nBufferSize
)
98 sal_Size nLen
= lclGetLen( pnPassData
, nBufferSize
);
100 sal_uInt16 nHash
= static_cast< sal_uInt16
>( nLen
);
104 const sal_uInt8
* pnChar
= pnPassData
;
105 for( sal_Size nIndex
= 0; nIndex
< nLen
; ++nIndex
, ++pnChar
)
107 sal_uInt16 cChar
= *pnChar
;
108 sal_uInt8 nRot
= static_cast< sal_uInt8
>( (nIndex
+ 1) % 15 );
109 lclRotateLeft( cChar
, nRot
, 15 );
118 // ============================================================================
120 MSCodec_Xor95::MSCodec_Xor95(int nRotateDistance
) :
124 mnRotateDistance( nRotateDistance
)
126 (void)memset( mpnKey
, 0, sizeof( mpnKey
) );
129 MSCodec_Xor95::~MSCodec_Xor95()
131 (void)memset( mpnKey
, 0, sizeof( mpnKey
) );
135 void MSCodec_Xor95::InitKey( const sal_uInt8 pnPassData
[ 16 ] )
137 mnKey
= lclGetKey( pnPassData
, 16 );
138 mnHash
= lclGetHash( pnPassData
, 16 );
140 (void)memcpy( mpnKey
, pnPassData
, 16 );
142 static const sal_uInt8 spnFillChars
[] =
144 0xBB, 0xFF, 0xFF, 0xBA,
145 0xFF, 0xFF, 0xB9, 0x80,
146 0x00, 0xBE, 0x0F, 0x00,
151 sal_Size nLen
= lclGetLen( pnPassData
, 16 );
152 const sal_uInt8
* pnFillChar
= spnFillChars
;
153 for( nIndex
= nLen
; nIndex
< sizeof( mpnKey
); ++nIndex
, ++pnFillChar
)
154 mpnKey
[ nIndex
] = *pnFillChar
;
157 ShortToSVBT16( mnKey
, pnOrigKey
);
158 sal_uInt8
* pnKeyChar
= mpnKey
;
159 for( nIndex
= 0; nIndex
< sizeof( mpnKey
); ++nIndex
, ++pnKeyChar
)
161 *pnKeyChar
^= pnOrigKey
[ nIndex
& 0x01 ];
162 lclRotateLeft( *pnKeyChar
, mnRotateDistance
);
166 sal_Bool
MSCodec_Xor95::InitCodec( const uno::Sequence
< beans::NamedValue
>& aData
)
168 sal_Bool bResult
= sal_False
;
170 ::comphelper::SequenceAsHashMap
aHashData( aData
);
171 uno::Sequence
< sal_Int8
> aKey
= aHashData
.getUnpackedValueOrDefault( OUString( "XOR95EncryptionKey" ), uno::Sequence
< sal_Int8
>() );
173 if ( aKey
.getLength() == 16 )
175 (void)memcpy( mpnKey
, aKey
.getConstArray(), 16 );
178 mnKey
= (sal_uInt16
)aHashData
.getUnpackedValueOrDefault( OUString( "XOR95BaseKey" ), (sal_Int16
)0 );
179 mnHash
= (sal_uInt16
)aHashData
.getUnpackedValueOrDefault( OUString( "XOR95PasswordHash" ), (sal_Int16
)0 );
182 OSL_FAIL( "Unexpected key size!\n" );
187 uno::Sequence
< beans::NamedValue
> MSCodec_Xor95::GetEncryptionData()
189 ::comphelper::SequenceAsHashMap aHashData
;
190 aHashData
[ OUString( "XOR95EncryptionKey" ) ] <<= uno::Sequence
<sal_Int8
>( (sal_Int8
*)mpnKey
, 16 );
191 aHashData
[ OUString( "XOR95BaseKey" ) ] <<= (sal_Int16
)mnKey
;
192 aHashData
[ OUString( "XOR95PasswordHash" ) ] <<= (sal_Int16
)mnHash
;
194 return aHashData
.getAsConstNamedValueList();
197 bool MSCodec_Xor95::VerifyKey( sal_uInt16 nKey
, sal_uInt16 nHash
) const
199 return (nKey
== mnKey
) && (nHash
== mnHash
);
202 void MSCodec_Xor95::InitCipher()
207 void MSCodec_XorXLS95::Decode( sal_uInt8
* pnData
, sal_Size nBytes
)
209 const sal_uInt8
* pnCurrKey
= mpnKey
+ mnOffset
;
210 const sal_uInt8
* pnKeyLast
= mpnKey
+ 0x0F;
212 for( const sal_uInt8
* pnDataEnd
= pnData
+ nBytes
; pnData
< pnDataEnd
; ++pnData
)
214 lclRotateLeft( *pnData
, 3 );
215 *pnData
^= *pnCurrKey
;
216 if( pnCurrKey
< pnKeyLast
) ++pnCurrKey
; else pnCurrKey
= mpnKey
;
223 void MSCodec_XorWord95::Decode( sal_uInt8
* pnData
, sal_Size nBytes
)
225 const sal_uInt8
* pnCurrKey
= mpnKey
+ mnOffset
;
226 const sal_uInt8
* pnKeyLast
= mpnKey
+ 0x0F;
228 for( const sal_uInt8
* pnDataEnd
= pnData
+ nBytes
; pnData
< pnDataEnd
; ++pnData
)
230 const sal_uInt8 cChar
= *pnData
^ *pnCurrKey
;
231 if (*pnData
&& cChar
)
234 if( pnCurrKey
< pnKeyLast
)
245 void MSCodec_Xor95::Skip( sal_Size nBytes
)
247 mnOffset
= (mnOffset
+ nBytes
) & 0x0F;
250 // ============================================================================
252 MSCodec_Std97::MSCodec_Std97 ()
254 m_hCipher
= rtl_cipher_create (
255 rtl_Cipher_AlgorithmARCFOUR
, rtl_Cipher_ModeStream
);
256 OSL_ASSERT(m_hCipher
!= 0);
258 m_hDigest
= rtl_digest_create (
259 rtl_Digest_AlgorithmMD5
);
260 OSL_ASSERT(m_hDigest
!= 0);
262 (void)memset (m_pDigestValue
, 0, sizeof(m_pDigestValue
));
263 (void)memset (m_pDocId
, 0, sizeof(m_pDocId
));
266 MSCodec_Std97::~MSCodec_Std97 ()
268 (void)memset (m_pDigestValue
, 0, sizeof(m_pDigestValue
));
269 (void)memset (m_pDocId
, 0, sizeof(m_pDocId
));
270 rtl_digest_destroy (m_hDigest
);
271 rtl_cipher_destroy (m_hCipher
);
274 #if DEBUG_MSO_ENCRYPTION_STD97
275 static void lcl_PrintDigest(const sal_uInt8
* pDigest
, const char* msg
)
277 printf("digest: (%s)\n", msg
);
278 for (int i
= 0; i
< 16; ++i
)
279 printf("%2.2x ", pDigest
[i
]);
283 static void lcl_PrintDigest(const sal_uInt8
* /*pDigest*/, const char* /*msg*/)
288 sal_Bool
MSCodec_Std97::InitCodec( const uno::Sequence
< beans::NamedValue
>& aData
)
290 #if DEBUG_MSO_ENCRYPTION_STD97
291 fprintf(stdout
, "MSCodec_Std97::InitCodec: --begin\n");fflush(stdout
);
293 sal_Bool bResult
= sal_False
;
295 ::comphelper::SequenceAsHashMap
aHashData( aData
);
296 uno::Sequence
< sal_Int8
> aKey
= aHashData
.getUnpackedValueOrDefault( OUString( "STD97EncryptionKey" ), uno::Sequence
< sal_Int8
>() );
298 if ( aKey
.getLength() == RTL_DIGEST_LENGTH_MD5
)
300 (void)memcpy( m_pDigestValue
, aKey
.getConstArray(), RTL_DIGEST_LENGTH_MD5
);
301 uno::Sequence
< sal_Int8
> aUniqueID
= aHashData
.getUnpackedValueOrDefault( OUString( "STD97UniqueID" ), uno::Sequence
< sal_Int8
>() );
302 if ( aUniqueID
.getLength() == 16 )
304 (void)memcpy( m_pDocId
, aUniqueID
.getConstArray(), 16 );
306 lcl_PrintDigest(m_pDigestValue
, "digest value");
307 lcl_PrintDigest(m_pDocId
, "DocId value");
310 OSL_FAIL( "Unexpected document ID!\n" );
313 OSL_FAIL( "Unexpected key size!\n" );
318 uno::Sequence
< beans::NamedValue
> MSCodec_Std97::GetEncryptionData()
320 ::comphelper::SequenceAsHashMap aHashData
;
321 aHashData
[ OUString( "STD97EncryptionKey" ) ] <<= uno::Sequence
< sal_Int8
>( (sal_Int8
*)m_pDigestValue
, RTL_DIGEST_LENGTH_MD5
);
322 aHashData
[ OUString( "STD97UniqueID" ) ] <<= uno::Sequence
< sal_Int8
>( (sal_Int8
*)m_pDocId
, 16 );
324 return aHashData
.getAsConstNamedValueList();
327 void MSCodec_Std97::InitKey (
328 const sal_uInt16 pPassData
[16],
329 const sal_uInt8 pDocId
[16])
331 #if DEBUG_MSO_ENCRYPTION_STD97
332 fprintf(stdout
, "MSCodec_Std97::InitKey: --begin\n");fflush(stdout
);
334 uno::Sequence
< sal_Int8
> aKey
= ::comphelper::DocPasswordHelper::GenerateStd97Key( pPassData
, uno::Sequence
< sal_Int8
>( (sal_Int8
*)pDocId
, 16 ) );
335 // Fill raw digest of above updates into DigestValue.
337 if ( aKey
.getLength() == sizeof(m_pDigestValue
) )
338 (void)memcpy ( m_pDigestValue
, aKey
.getConstArray(), sizeof(m_pDigestValue
) );
340 memset( m_pDigestValue
, 0, sizeof(m_pDigestValue
) );
342 lcl_PrintDigest(m_pDigestValue
, "digest value");
344 (void)memcpy (m_pDocId
, pDocId
, 16);
346 lcl_PrintDigest(m_pDocId
, "DocId value");
349 bool MSCodec_Std97::VerifyKey (
350 const sal_uInt8 pSaltData
[16],
351 const sal_uInt8 pSaltDigest
[16])
353 // both the salt data and salt digest (hash) come from the document being imported.
355 #if DEBUG_MSO_ENCRYPTION_STD97
356 fprintf(stdout
, "MSCodec_Std97::VerifyKey: \n");
357 lcl_PrintDigest(pSaltData
, "salt data");
358 lcl_PrintDigest(pSaltDigest
, "salt hash");
364 sal_uInt8 pDigest
[RTL_DIGEST_LENGTH_MD5
];
365 GetDigestFromSalt(pSaltData
, pDigest
);
367 sal_uInt8 pBuffer
[16];
368 // Decode original SaltDigest into Buffer.
370 m_hCipher
, pSaltDigest
, 16, pBuffer
, sizeof(pBuffer
));
372 // Compare Buffer with computed Digest.
373 result
= (memcmp (pBuffer
, pDigest
, sizeof(pDigest
)) == 0);
375 // Erase Buffer and Digest arrays.
376 (void)memset (pBuffer
, 0, sizeof(pBuffer
));
377 (void)memset (pDigest
, 0, sizeof(pDigest
));
383 bool MSCodec_Std97::InitCipher (sal_uInt32 nCounter
)
385 rtlCipherError result
;
386 sal_uInt8 pKeyData
[64]; // 512-bit message block
388 // Initialize KeyData array.
389 (void)memset (pKeyData
, 0, sizeof(pKeyData
));
391 // Fill 40 bit of DigestValue into [0..4].
392 (void)memcpy (pKeyData
, m_pDigestValue
, 5);
394 // Fill counter into [5..8].
395 pKeyData
[ 5] = sal_uInt8((nCounter
>> 0) & 0xff);
396 pKeyData
[ 6] = sal_uInt8((nCounter
>> 8) & 0xff);
397 pKeyData
[ 7] = sal_uInt8((nCounter
>> 16) & 0xff);
398 pKeyData
[ 8] = sal_uInt8((nCounter
>> 24) & 0xff);
403 // Fill raw digest of KeyData into KeyData.
404 (void)rtl_digest_updateMD5 (
405 m_hDigest
, pKeyData
, sizeof(pKeyData
));
406 (void)rtl_digest_rawMD5 (
407 m_hDigest
, pKeyData
, RTL_DIGEST_LENGTH_MD5
);
409 // Initialize Cipher with KeyData (for decoding).
410 result
= rtl_cipher_init (
411 m_hCipher
, rtl_Cipher_DirectionBoth
,
412 pKeyData
, RTL_DIGEST_LENGTH_MD5
, 0, 0);
414 // Erase KeyData array and leave.
415 (void)memset (pKeyData
, 0, sizeof(pKeyData
));
417 return (result
== rtl_Cipher_E_None
);
420 bool MSCodec_Std97::CreateSaltDigest( const sal_uInt8 nSaltData
[16], sal_uInt8 nSaltDigest
[16] )
422 #if DEBUG_MSO_ENCRYPTION_STD97
423 lcl_PrintDigest(nSaltData
, "salt data");
429 sal_uInt8 pDigest
[RTL_DIGEST_LENGTH_MD5
];
430 GetDigestFromSalt(nSaltData
, pDigest
);
433 m_hCipher
, pDigest
, 16, pDigest
, sizeof(pDigest
));
435 (void)memcpy(nSaltDigest
, pDigest
, 16);
441 bool MSCodec_Std97::Encode (
442 const void *pData
, sal_Size nDatLen
,
443 sal_uInt8
*pBuffer
, sal_Size nBufLen
)
445 rtlCipherError result
;
447 result
= rtl_cipher_encode (
448 m_hCipher
, pData
, nDatLen
, pBuffer
, nBufLen
);
450 return (result
== rtl_Cipher_E_None
);
453 bool MSCodec_Std97::Decode (
454 const void *pData
, sal_Size nDatLen
,
455 sal_uInt8
*pBuffer
, sal_Size nBufLen
)
457 rtlCipherError result
;
459 result
= rtl_cipher_decode (
460 m_hCipher
, pData
, nDatLen
, pBuffer
, nBufLen
);
462 return (result
== rtl_Cipher_E_None
);
465 bool MSCodec_Std97::Skip( sal_Size nDatLen
)
467 sal_uInt8 pnDummy
[ 1024 ];
468 sal_Size nDatLeft
= nDatLen
;
471 while (bResult
&& nDatLeft
)
473 sal_Size nBlockLen
= ::std::min
< sal_Size
>( nDatLeft
, sizeof(pnDummy
) );
474 bResult
= Decode( pnDummy
, nBlockLen
, pnDummy
, nBlockLen
);
475 nDatLeft
-= nBlockLen
;
481 void MSCodec_Std97::GetDigestFromSalt( const sal_uInt8 pSaltData
[16], sal_uInt8 pDigest
[16] )
483 sal_uInt8 pBuffer
[64];
484 sal_uInt8 pDigestLocal
[16];
486 // Decode SaltData into Buffer.
488 m_hCipher
, pSaltData
, 16, pBuffer
, sizeof(pBuffer
));
490 // set the 129th bit to make the buffer 128-bit in length.
493 // erase the rest of the buffer with zeros.
494 (void)memset (pBuffer
+ 17, 0, sizeof(pBuffer
) - 17);
496 // set the 441st bit.
499 // Fill raw digest of Buffer into Digest.
500 rtl_digest_updateMD5 (
501 m_hDigest
, pBuffer
, sizeof(pBuffer
));
503 m_hDigest
, pDigestLocal
, sizeof(pDigestLocal
));
505 memcpy(pDigest
, pDigestLocal
, 16);
508 void MSCodec_Std97::GetEncryptKey (
509 const sal_uInt8 pSalt
[16],
510 sal_uInt8 pSaltData
[16],
511 sal_uInt8 pSaltDigest
[16])
515 sal_uInt8 pDigest
[RTL_DIGEST_LENGTH_MD5
];
516 sal_uInt8 pBuffer
[64];
519 m_hCipher
, pSalt
, 16, pSaltData
, sizeof(pBuffer
));
521 (void)memcpy( pBuffer
, pSalt
, 16 );
524 (void)memset (pBuffer
+ 17, 0, sizeof(pBuffer
) - 17);
527 rtl_digest_updateMD5 (
528 m_hDigest
, pBuffer
, sizeof(pBuffer
));
530 m_hDigest
, pDigest
, sizeof(pDigest
));
533 m_hCipher
, pDigest
, 16, pSaltDigest
, 16);
535 (void)memset (pBuffer
, 0, sizeof(pBuffer
));
536 (void)memset (pDigest
, 0, sizeof(pDigest
));
540 void MSCodec_Std97::GetDocId( sal_uInt8 pDocId
[16] )
542 if ( sizeof( m_pDocId
) == 16 )
543 (void)memcpy( pDocId
, m_pDocId
, 16 );
546 // ============================================================================
550 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */