xref: /trunk/main/xmlsecurity/source/xmlsec/nss/ciphercontext.cxx (revision 45c5a00162811bd03b87fcb4fe9996782f2b59ec)
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() ) ), static_cast<unsigned>(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() ) ), static_cast<unsigned>(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 {
99     ::osl::MutexGuard aGuard( m_aMutex );
100 
101     if ( m_bBroken )
102         throw uno::RuntimeException();
103 
104     if ( m_bDisposed )
105         throw lang::DisposedException();
106 
107     uno::Sequence< sal_Int8 > aToConvert;
108     if ( aData.getLength() )
109     {
110         sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength();
111         OSL_ENSURE( nOldLastBlockLen <= m_nBlockSize, "Unexpected last block size!" );
112 
113         sal_Int32 nAvailableData = nOldLastBlockLen + aData.getLength();
114         sal_Int32 nToConvertLen = nAvailableData;
115         if ( m_bEncryption || !m_bW3CPadding )
116         {
117             if ( nAvailableData % m_nBlockSize == 0 )
118                 nToConvertLen = nAvailableData;
119             else if ( nAvailableData < m_nBlockSize )
120                 nToConvertLen = 0;
121             else
122                 nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize;
123         }
124         else
125         {
126             // decryption with W3C padding needs at least one block for finalizing
127             if ( nAvailableData < m_nBlockSize * 2 )
128                 nToConvertLen = 0;
129             else
130                 nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize - m_nBlockSize;
131         }
132 
133         aToConvert.realloc( nToConvertLen );
134         if ( nToConvertLen == 0 )
135         {
136             m_aLastBlock.realloc( nOldLastBlockLen + aData.getLength() );
137             rtl_copyMemory( m_aLastBlock.getArray() + nOldLastBlockLen, aData.getConstArray(), aData.getLength() );
138             // aToConvert stays empty
139         }
140         else if ( nToConvertLen < nOldLastBlockLen )
141         {
142             rtl_copyMemory( aToConvert.getArray(), m_aLastBlock.getConstArray(), nToConvertLen );
143             rtl_copyMemory( m_aLastBlock.getArray(), m_aLastBlock.getConstArray() + nToConvertLen, nOldLastBlockLen - nToConvertLen );
144             m_aLastBlock.realloc( nOldLastBlockLen - nToConvertLen + aData.getLength() );
145             rtl_copyMemory( m_aLastBlock.getArray() + nOldLastBlockLen - nToConvertLen, aData.getConstArray(), aData.getLength() );
146         }
147         else
148         {
149             rtl_copyMemory( aToConvert.getArray(), m_aLastBlock.getConstArray(), nOldLastBlockLen );
150             if ( nToConvertLen > nOldLastBlockLen )
151                 rtl_copyMemory( aToConvert.getArray() + nOldLastBlockLen, aData.getConstArray(), nToConvertLen - nOldLastBlockLen );
152             m_aLastBlock.realloc( nAvailableData - nToConvertLen );
153             rtl_copyMemory( m_aLastBlock.getArray(), aData.getConstArray() + nToConvertLen - nOldLastBlockLen, nAvailableData - nToConvertLen );
154         }
155     }
156 
157     uno::Sequence< sal_Int8 > aResult;
158     OSL_ENSURE( aToConvert.getLength() % m_nBlockSize == 0, "Unexpected size of the data to encrypt!" );
159     if ( aToConvert.getLength() )
160     {
161         int nResultLen = 0;
162         aResult.realloc( aToConvert.getLength() + m_nBlockSize );
163         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 )
164         {
165             m_bBroken = true;
166             Dispose();
167             throw uno::RuntimeException();
168         }
169 
170         m_nConverted += aToConvert.getLength();
171         aResult.realloc( nResultLen );
172     }
173 
174     return aResult;
175 }
176 
177 uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::finalizeCipherContextAndDispose()
178 {
179     ::osl::MutexGuard aGuard( m_aMutex );
180 
181     if ( m_bBroken )
182         throw uno::RuntimeException();
183 
184     if ( m_bDisposed )
185         throw lang::DisposedException();
186 
187     OSL_ENSURE( m_nBlockSize <= SAL_MAX_INT8, "Unexpected block size!" );
188     OSL_ENSURE( m_nConverted % m_nBlockSize == 0, "Unexpected amount of bytes is already converted!" );
189     sal_Int32 nSizeForPadding = ( m_nConverted + m_aLastBlock.getLength() ) % m_nBlockSize;
190 
191     // if it is decryption, the amount of data should be rounded to the block size even in case of padding
192     if ( ( !m_bPadding || !m_bEncryption ) && nSizeForPadding )
193         throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "The data should contain complete blocks only." ) ), uno::Reference< uno::XInterface >() );
194 
195     if ( m_bW3CPadding && m_bEncryption )
196     {
197         // in this case the last block should be smaller than standard block
198         // it will be increased with the padding
199         OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize, "Unexpected size of cashed incomplete last block!" );
200 
201         // W3CPadding handling for encryption
202         sal_Int32 nPaddingSize = m_nBlockSize - nSizeForPadding;
203         sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength();
204         m_aLastBlock.realloc( nOldLastBlockLen + nPaddingSize );
205 
206         if ( nPaddingSize > 1 )
207         {
208             TimeValue aTime;
209             osl_getSystemTime( &aTime );
210             rtlRandomPool aRandomPool = rtl_random_createPool();
211             rtl_random_addBytes( aRandomPool, &aTime, 8 );
212             rtl_random_getBytes( aRandomPool, m_aLastBlock.getArray() + nOldLastBlockLen, nPaddingSize - 1 );
213             rtl_random_destroyPool ( aRandomPool );
214         }
215         m_aLastBlock[m_aLastBlock.getLength() - 1] = static_cast< sal_Int8 >( nPaddingSize );
216     }
217 
218     // finally should the last block be smaller than two standard blocks
219     OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize * 2 , "Unexpected size of cashed incomplete last block!" );
220 
221     uno::Sequence< sal_Int8 > aResult;
222     if ( m_aLastBlock.getLength() )
223     {
224         int nPrefResLen = 0;
225         aResult.realloc( m_aLastBlock.getLength() + m_nBlockSize );
226         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 )
227         {
228             m_bBroken = true;
229             Dispose();
230             throw uno::RuntimeException();
231         }
232 
233         aResult.realloc( nPrefResLen );
234         m_aLastBlock.realloc( 0 );
235     }
236 
237     sal_Int32 nPrefixLen = aResult.getLength();
238     aResult.realloc( nPrefixLen + m_nBlockSize * 2 );
239     unsigned nFinalLen = 0;
240     if ( PK11_DigestFinal( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() + nPrefixLen ), &nFinalLen, aResult.getLength() - nPrefixLen ) != SECSuccess )
241     {
242         m_bBroken = true;
243         Dispose();
244         throw uno::RuntimeException();
245     }
246 
247     aResult.realloc( nPrefixLen + nFinalLen );
248 
249     if ( m_bW3CPadding && !m_bEncryption )
250     {
251         // W3CPadding handling for decryption
252         // aResult should have enough data, since we let m_aLastBlock be big enough in case of decryption
253         OSL_ENSURE( aResult.getLength() >= m_nBlockSize, "Not enough data to handle the padding!" );
254 
255         sal_Int8 nBytesToRemove = aResult[aResult.getLength() - 1];
256         if ( nBytesToRemove <= 0 || nBytesToRemove > aResult.getLength() )
257         {
258             m_bBroken = true;
259             Dispose();
260             throw uno::RuntimeException();
261         }
262 
263         aResult.realloc( aResult.getLength() - nBytesToRemove );
264     }
265 
266     Dispose();
267 
268     return aResult;
269 }
270