| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include <precompiled_xmlsecurity.hxx> |
| |
| #include <osl/time.h> |
| #include <rtl/random.h> |
| #include <rtl/ref.hxx> |
| |
| #include "ciphercontext.hxx" |
| |
| using namespace ::com::sun::star; |
| |
| 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 ) |
| { |
| ::rtl::Reference< OCipherContext > xResult = new OCipherContext; |
| |
| xResult->m_pSlot = PK11_GetBestSlot( nNSSCipherID, NULL ); |
| if ( xResult->m_pSlot ) |
| { |
| SECItem aKeyItem = { siBuffer, const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( aKey.getConstArray() ) ), static_cast<unsigned>(aKey.getLength()) }; |
| xResult->m_pSymKey = PK11_ImportSymKey( xResult->m_pSlot, nNSSCipherID, PK11_OriginDerive, bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, &aKeyItem, NULL ); |
| if ( xResult->m_pSymKey ) |
| { |
| SECItem aIVItem = { siBuffer, const_cast< unsigned char* >( reinterpret_cast< const unsigned char* >( aInitializationVector.getConstArray() ) ), static_cast<unsigned>(aInitializationVector.getLength()) }; |
| xResult->m_pSecParam = PK11_ParamFromIV( nNSSCipherID, &aIVItem ); |
| if ( xResult->m_pSecParam ) |
| { |
| xResult->m_pContext = PK11_CreateContextBySymKey( nNSSCipherID, bEncryption ? CKA_ENCRYPT : CKA_DECRYPT, xResult->m_pSymKey, xResult->m_pSecParam); |
| if ( xResult->m_pContext ) |
| { |
| xResult->m_bEncryption = bEncryption; |
| xResult->m_bW3CPadding = bW3CPadding; |
| xResult->m_bPadding = bW3CPadding || ( PK11_GetPadMechanism( nNSSCipherID ) == nNSSCipherID ); |
| xResult->m_nBlockSize = PK11_GetBlockSize( nNSSCipherID, xResult->m_pSecParam ); |
| if ( xResult->m_nBlockSize <= SAL_MAX_INT8 ) |
| return xResult.get(); |
| } |
| } |
| } |
| } |
| |
| return uno::Reference< xml::crypto::XCipherContext >(); |
| } |
| |
| void OCipherContext::Dispose() |
| { |
| ::osl::MutexGuard aGuard( m_aMutex ); |
| |
| if ( m_pContext ) |
| { |
| PK11_DestroyContext( m_pContext, PR_TRUE ); |
| m_pContext = NULL; |
| } |
| |
| if ( m_pSecParam ) |
| { |
| SECITEM_FreeItem( m_pSecParam, PR_TRUE ); |
| m_pSecParam = NULL; |
| } |
| |
| if ( m_pSymKey ) |
| { |
| PK11_FreeSymKey( m_pSymKey ); |
| m_pSymKey = NULL; |
| } |
| |
| if ( m_pSlot ) |
| { |
| PK11_FreeSlot( m_pSlot ); |
| m_pSlot = NULL; |
| } |
| |
| m_bDisposed = true; |
| } |
| |
| uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::convertWithCipherContext( const uno::Sequence< ::sal_Int8 >& aData ) |
| throw ( lang::IllegalArgumentException, lang::DisposedException, uno::RuntimeException) |
| { |
| ::osl::MutexGuard aGuard( m_aMutex ); |
| |
| if ( m_bBroken ) |
| throw uno::RuntimeException(); |
| |
| if ( m_bDisposed ) |
| throw lang::DisposedException(); |
| |
| uno::Sequence< sal_Int8 > aToConvert; |
| if ( aData.getLength() ) |
| { |
| sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength(); |
| OSL_ENSURE( nOldLastBlockLen <= m_nBlockSize, "Unexpected last block size!" ); |
| |
| sal_Int32 nAvailableData = nOldLastBlockLen + aData.getLength(); |
| sal_Int32 nToConvertLen = nAvailableData; |
| if ( m_bEncryption || !m_bW3CPadding ) |
| { |
| if ( nAvailableData % m_nBlockSize == 0 ) |
| nToConvertLen = nAvailableData; |
| else if ( nAvailableData < m_nBlockSize ) |
| nToConvertLen = 0; |
| else |
| nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize; |
| } |
| else |
| { |
| // decryption with W3C padding needs at least one block for finalizing |
| if ( nAvailableData < m_nBlockSize * 2 ) |
| nToConvertLen = 0; |
| else |
| nToConvertLen = nAvailableData - nAvailableData % m_nBlockSize - m_nBlockSize; |
| } |
| |
| aToConvert.realloc( nToConvertLen ); |
| if ( nToConvertLen == 0 ) |
| { |
| m_aLastBlock.realloc( nOldLastBlockLen + aData.getLength() ); |
| rtl_copyMemory( m_aLastBlock.getArray() + nOldLastBlockLen, aData.getConstArray(), aData.getLength() ); |
| // aToConvert stays empty |
| } |
| else if ( nToConvertLen < nOldLastBlockLen ) |
| { |
| rtl_copyMemory( aToConvert.getArray(), m_aLastBlock.getConstArray(), nToConvertLen ); |
| rtl_copyMemory( m_aLastBlock.getArray(), m_aLastBlock.getConstArray() + nToConvertLen, nOldLastBlockLen - nToConvertLen ); |
| m_aLastBlock.realloc( nOldLastBlockLen - nToConvertLen + aData.getLength() ); |
| rtl_copyMemory( m_aLastBlock.getArray() + nOldLastBlockLen - nToConvertLen, aData.getConstArray(), aData.getLength() ); |
| } |
| else |
| { |
| rtl_copyMemory( aToConvert.getArray(), m_aLastBlock.getConstArray(), nOldLastBlockLen ); |
| if ( nToConvertLen > nOldLastBlockLen ) |
| rtl_copyMemory( aToConvert.getArray() + nOldLastBlockLen, aData.getConstArray(), nToConvertLen - nOldLastBlockLen ); |
| m_aLastBlock.realloc( nAvailableData - nToConvertLen ); |
| rtl_copyMemory( m_aLastBlock.getArray(), aData.getConstArray() + nToConvertLen - nOldLastBlockLen, nAvailableData - nToConvertLen ); |
| } |
| } |
| |
| uno::Sequence< sal_Int8 > aResult; |
| OSL_ENSURE( aToConvert.getLength() % m_nBlockSize == 0, "Unexpected size of the data to encrypt!" ); |
| if ( aToConvert.getLength() ) |
| { |
| int nResultLen = 0; |
| aResult.realloc( aToConvert.getLength() + m_nBlockSize ); |
| 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 ) |
| { |
| m_bBroken = true; |
| Dispose(); |
| throw uno::RuntimeException(); |
| } |
| |
| m_nConverted += aToConvert.getLength(); |
| aResult.realloc( nResultLen ); |
| } |
| |
| return aResult; |
| } |
| |
| uno::Sequence< ::sal_Int8 > SAL_CALL OCipherContext::finalizeCipherContextAndDispose() |
| throw (lang::DisposedException, uno::RuntimeException) |
| { |
| ::osl::MutexGuard aGuard( m_aMutex ); |
| |
| if ( m_bBroken ) |
| throw uno::RuntimeException(); |
| |
| if ( m_bDisposed ) |
| throw lang::DisposedException(); |
| |
| OSL_ENSURE( m_nBlockSize <= SAL_MAX_INT8, "Unexpected block size!" ); |
| OSL_ENSURE( m_nConverted % m_nBlockSize == 0, "Unexpected amount of bytes is already converted!" ); |
| sal_Int32 nSizeForPadding = ( m_nConverted + m_aLastBlock.getLength() ) % m_nBlockSize; |
| |
| // if it is decryption, the amount of data should be rounded to the block size even in case of padding |
| if ( ( !m_bPadding || !m_bEncryption ) && nSizeForPadding ) |
| throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "The data should contain complete blocks only." ) ), uno::Reference< uno::XInterface >() ); |
| |
| if ( m_bW3CPadding && m_bEncryption ) |
| { |
| // in this case the last block should be smaller than standtard block |
| // it will be increased with the padding |
| OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize, "Unexpected size of cashed incomplete last block!" ); |
| |
| // W3CPadding handling for encryption |
| sal_Int32 nPaddingSize = m_nBlockSize - nSizeForPadding; |
| sal_Int32 nOldLastBlockLen = m_aLastBlock.getLength(); |
| m_aLastBlock.realloc( nOldLastBlockLen + nPaddingSize ); |
| |
| if ( nPaddingSize > 1 ) |
| { |
| TimeValue aTime; |
| osl_getSystemTime( &aTime ); |
| rtlRandomPool aRandomPool = rtl_random_createPool(); |
| rtl_random_addBytes( aRandomPool, &aTime, 8 ); |
| rtl_random_getBytes( aRandomPool, m_aLastBlock.getArray() + nOldLastBlockLen, nPaddingSize - 1 ); |
| rtl_random_destroyPool ( aRandomPool ); |
| } |
| m_aLastBlock[m_aLastBlock.getLength() - 1] = static_cast< sal_Int8 >( nPaddingSize ); |
| } |
| |
| // finally should the last block be smaller than two standard blocks |
| OSL_ENSURE( m_aLastBlock.getLength() < m_nBlockSize * 2 , "Unexpected size of cashed incomplete last block!" ); |
| |
| uno::Sequence< sal_Int8 > aResult; |
| if ( m_aLastBlock.getLength() ) |
| { |
| int nPrefResLen = 0; |
| aResult.realloc( m_aLastBlock.getLength() + m_nBlockSize ); |
| 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 ) |
| { |
| m_bBroken = true; |
| Dispose(); |
| throw uno::RuntimeException(); |
| } |
| |
| aResult.realloc( nPrefResLen ); |
| m_aLastBlock.realloc( 0 ); |
| } |
| |
| sal_Int32 nPrefixLen = aResult.getLength(); |
| aResult.realloc( nPrefixLen + m_nBlockSize * 2 ); |
| unsigned nFinalLen = 0; |
| if ( PK11_DigestFinal( m_pContext, reinterpret_cast< unsigned char* >( aResult.getArray() + nPrefixLen ), &nFinalLen, aResult.getLength() - nPrefixLen ) != SECSuccess ) |
| { |
| m_bBroken = true; |
| Dispose(); |
| throw uno::RuntimeException(); |
| } |
| |
| aResult.realloc( nPrefixLen + nFinalLen ); |
| |
| if ( m_bW3CPadding && !m_bEncryption ) |
| { |
| // W3CPadding handling for decryption |
| // aResult should have anough data, since we let m_aLastBlock be big enough in case of decryption |
| OSL_ENSURE( aResult.getLength() >= m_nBlockSize, "Not enough data to handle the padding!" ); |
| |
| sal_Int8 nBytesToRemove = aResult[aResult.getLength() - 1]; |
| if ( nBytesToRemove <= 0 || nBytesToRemove > aResult.getLength() ) |
| { |
| m_bBroken = true; |
| Dispose(); |
| throw uno::RuntimeException(); |
| } |
| |
| aResult.realloc( aResult.getLength() - nBytesToRemove ); |
| } |
| |
| Dispose(); |
| |
| return aResult; |
| } |
| |