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