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
Create(CK_MECHANISM_TYPE nNSSCipherID,const uno::Sequence<::sal_Int8> & aKey,const uno::Sequence<::sal_Int8> & aInitializationVector,bool bEncryption,bool bW3CPadding)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
Dispose()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
convertWithCipherContext(const uno::Sequence<::sal_Int8> & aData)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
finalizeCipherContextAndDispose()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