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