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