1  /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
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.
14  *
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).
20  *
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.
25  *
26  ************************************************************************/
27 
28 #include <precompiled_xmlsecurity.hxx>
29 
30 #include <osl/time.h>
31 #include <rtl/random.h>
32 #include <rtl/ref.hxx>
33 
34 #include "ciphercontext.hxx"
35 
36 using namespace ::com::sun::star;
37 
38 uno::Reference< xml::crypto::XCipherContext > OCipherContext::Create( CK_MECHANISM_TYPE nNSSCipherID, const uno::Sequence< ::sal_Int8 >& aKey, const uno::Sequence< ::sal_Int8 >& aInitializationVector, bool bEncryption, bool bW3CPadding )
39 {
40     ::rtl::Reference< OCipherContext > xResult = new OCipherContext;
41 
42     xResult->m_pSlot = PK11_GetBestSlot( nNSSCipherID, NULL );
43     if ( xResult->m_pSlot )
44     {
45         SECItem aKeyItem = { siBuffer, const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( aKey.getConstArray() ) ), aKey.getLength() };
46         xResult->m_pSymKey = PK11_ImportSymKey( xResult->m_pSlot, nNSSCipherID, PK11_OriginDerive, bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, &aKeyItem, NULL );
47         if ( xResult->m_pSymKey )
48         {
49             SECItem aIVItem = { siBuffer, const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( aInitializationVector.getConstArray() ) ), aInitializationVector.getLength() };
50             xResult->m_pSecParam = PK11_ParamFromIV( nNSSCipherID, &aIVItem );
51             if ( xResult->m_pSecParam )
52             {
53                 xResult->m_pContext = PK11_CreateContextBySymKey( nNSSCipherID, bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, xResult->m_pSymKey, xResult->m_pSecParam);
54                 if ( xResult->m_pContext )
55                 {
56                     xResult->m_bEncryption = bEncryption;
57                     xResult->m_bW3CPadding = bW3CPadding;
58                     xResult->m_bPadding = bW3CPadding || ( PK11_GetPadMechanism( nNSSCipherID ) == nNSSCipherID );
59                     xResult->m_nBlockSize = PK11_GetBlockSize( nNSSCipherID, xResult->m_pSecParam );
60                     if ( xResult->m_nBlockSize <= SAL_MAX_INT8 )
61                         return xResult.get();
62                 }
63             }
64         }
65     }
66 
67     return uno::Reference< xml::crypto::XCipherContext >();
68 }
69 
70 void OCipherContext::Dispose()
71 {
72     ::osl::MutexGuard aGuard( m_aMutex );
73 
74     if ( m_pContext )
75     {
76         PK11_DestroyContext( m_pContext, PR_TRUE );
77         m_pContext = NULL;
78     }
79 
80     if ( m_pSecParam )
81     {
82         SECITEM_FreeItem( m_pSecParam, PR_TRUE );
83         m_pSecParam = NULL;
84     }
85 
86     if ( m_pSymKey )
87     {
88         PK11_FreeSymKey( m_pSymKey );
89         m_pSymKey = NULL;
90     }
91 
92     if ( m_pSlot )
93     {
94         PK11_FreeSlot( m_pSlot );
95         m_pSlot = NULL;
96     }
97 
98     m_bDisposed = true;
99 }
100 
101 uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::convertWithCipherContext( const uno::Sequence< ::sal_Int8 >& aData )
102     throw ( lang::IllegalArgumentException, lang::DisposedException, uno::RuntimeException)
103 {
104     ::osl::MutexGuard aGuard( m_aMutex );
105 
106     if ( m_bBroken )
107         throw uno::RuntimeException();
108 
109     if ( m_bDisposed )
110         throw lang::DisposedException();
111 
112     uno::Sequence< sal_Int8 > aToConvert;
113     if ( aData.getLength() )
114     {
115         sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength();
116         OSL_ENSURE( nOldLastBlockLen <= m_nBlockSize, "Unexpected last block size!" );
117 
118         sal_Int32 nAvailableData = nOldLastBlockLen + aData.getLength();
119         sal_Int32 nToConvertLen = nAvailableData;
120         if ( m_bEncryption || !m_bW3CPadding )
121         {
122             if ( nAvailableData % m_nBlockSize == 0 )
123                 nToConvertLen = nAvailableData;
124             else if ( nAvailableData < m_nBlockSize )
125                 nToConvertLen = 0;
126             else
127                 nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize;
128         }
129         else
130         {
131             // decryption with W3C padding needs at least one block for finalizing
132             if ( nAvailableData < m_nBlockSize * 2 )
133                 nToConvertLen = 0;
134             else
135                 nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize - m_nBlockSize;
136         }
137 
138         aToConvert.realloc( nToConvertLen );
139         if ( nToConvertLen == 0 )
140         {
141             m_aLastBlock.realloc( nOldLastBlockLen + aData.getLength() );
142             rtl_copyMemory( m_aLastBlock.getArray() + nOldLastBlockLen, aData.getConstArray(), aData.getLength() );
143             // aToConvert stays empty
144         }
145         else if ( nToConvertLen < nOldLastBlockLen )
146         {
147             rtl_copyMemory( aToConvert.getArray(), m_aLastBlock.getConstArray(), nToConvertLen );
148             rtl_copyMemory( m_aLastBlock.getArray(), m_aLastBlock.getConstArray() + nToConvertLen, nOldLastBlockLen - nToConvertLen );
149             m_aLastBlock.realloc( nOldLastBlockLen - nToConvertLen + aData.getLength() );
150             rtl_copyMemory( m_aLastBlock.getArray() + nOldLastBlockLen - nToConvertLen, aData.getConstArray(), aData.getLength() );
151         }
152         else
153         {
154             rtl_copyMemory( aToConvert.getArray(), m_aLastBlock.getConstArray(), nOldLastBlockLen );
155             if ( nToConvertLen > nOldLastBlockLen )
156                 rtl_copyMemory( aToConvert.getArray() + nOldLastBlockLen, aData.getConstArray(), nToConvertLen - nOldLastBlockLen );
157             m_aLastBlock.realloc( nAvailableData - nToConvertLen );
158             rtl_copyMemory( m_aLastBlock.getArray(), aData.getConstArray() + nToConvertLen - nOldLastBlockLen, nAvailableData - nToConvertLen );
159         }
160     }
161 
162     uno::Sequence< sal_Int8 > aResult;
163     OSL_ENSURE( aToConvert.getLength() % m_nBlockSize == 0, "Unexpected size of the data to encrypt!" );
164     if ( aToConvert.getLength() )
165     {
166         int nResultLen = 0;
167         aResult.realloc( aToConvert.getLength() + m_nBlockSize );
168         if ( PK11_CipherOp( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() ), &nResultLen, aResult.getLength(), const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( aToConvert.getConstArray() ) ), aToConvert.getLength() ) != SECSuccess )
169         {
170             m_bBroken = true;
171             Dispose();
172             throw uno::RuntimeException();
173         }
174 
175         m_nConverted += aToConvert.getLength();
176         aResult.realloc( nResultLen );
177     }
178 
179     return aResult;
180 }
181 
182 uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::finalizeCipherContextAndDispose()
183     throw (lang::DisposedException, uno::RuntimeException)
184 {
185     ::osl::MutexGuard aGuard( m_aMutex );
186 
187     if ( m_bBroken )
188         throw uno::RuntimeException();
189 
190     if ( m_bDisposed )
191         throw lang::DisposedException();
192 
193     OSL_ENSURE( m_nBlockSize <= SAL_MAX_INT8, "Unexpected block size!" );
194     OSL_ENSURE( m_nConverted % m_nBlockSize == 0, "Unexpected amount of bytes is already converted!" );
195     sal_Int32 nSizeForPadding = ( m_nConverted + m_aLastBlock.getLength() ) % m_nBlockSize;
196 
197     // if it is decryption, the amount of data should be rounded to the block size even in case of padding
198     if ( ( !m_bPadding || !m_bEncryption ) && nSizeForPadding )
199         throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "The data should contain complete blocks only." ) ), uno::Reference< uno::XInterface >() );
200 
201     if ( m_bW3CPadding && m_bEncryption )
202     {
203         // in this case the last block should be smaller than standtard block
204         // it will be increased with the padding
205         OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize, "Unexpected size of cashed incomplete last block!" );
206 
207         // W3CPadding handling for encryption
208         sal_Int32 nPaddingSize = m_nBlockSize - nSizeForPadding;
209         sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength();
210         m_aLastBlock.realloc( nOldLastBlockLen + nPaddingSize );
211 
212         if ( nPaddingSize > 1 )
213         {
214             TimeValue aTime;
215             osl_getSystemTime( &aTime );
216             rtlRandomPool aRandomPool = rtl_random_createPool();
217             rtl_random_addBytes( aRandomPool, &aTime, 8 );
218             rtl_random_getBytes( aRandomPool, m_aLastBlock.getArray() + nOldLastBlockLen, nPaddingSize - 1 );
219             rtl_random_destroyPool ( aRandomPool );
220         }
221         m_aLastBlock[m_aLastBlock.getLength() - 1] = static_cast< sal_Int8 >( nPaddingSize );
222     }
223 
224     // finally should the last block be smaller than two standard blocks
225     OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize * 2 , "Unexpected size of cashed incomplete last block!" );
226 
227     uno::Sequence< sal_Int8 > aResult;
228     if ( m_aLastBlock.getLength() )
229     {
230         int nPrefResLen = 0;
231         aResult.realloc( m_aLastBlock.getLength() + m_nBlockSize );
232         if ( PK11_CipherOp( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() ), &nPrefResLen, aResult.getLength(), const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( m_aLastBlock.getConstArray() ) ), m_aLastBlock.getLength() ) != SECSuccess )
233         {
234             m_bBroken = true;
235             Dispose();
236             throw uno::RuntimeException();
237         }
238 
239         aResult.realloc( nPrefResLen );
240         m_aLastBlock.realloc( 0 );
241     }
242 
243     sal_Int32 nPrefixLen = aResult.getLength();
244     aResult.realloc( nPrefixLen + m_nBlockSize * 2 );
245     unsigned nFinalLen = 0;
246     if ( PK11_DigestFinal( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() + nPrefixLen ), &nFinalLen, aResult.getLength() - nPrefixLen ) != SECSuccess )
247     {
248         m_bBroken = true;
249         Dispose();
250         throw uno::RuntimeException();
251     }
252 
253     aResult.realloc( nPrefixLen + nFinalLen );
254 
255     if ( m_bW3CPadding && !m_bEncryption )
256     {
257         // W3CPadding handling for decryption
258         // aResult should have anough data, since we let m_aLastBlock be big enough in case of decryption
259         OSL_ENSURE( aResult.getLength() >= m_nBlockSize, "Not enough data to handle the padding!" );
260 
261         sal_Int8 nBytesToRemove = aResult[aResult.getLength() - 1];
262         if ( nBytesToRemove <= 0 || nBytesToRemove > aResult.getLength() )
263         {
264             m_bBroken = true;
265             Dispose();
266             throw uno::RuntimeException();
267         }
268 
269         aResult.realloc( aResult.getLength() - nBytesToRemove );
270     }
271 
272     Dispose();
273 
274     return aResult;
275 }
276 
277