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