diff -r e8e63152f320 -r 2a9601315dfc javaextensions/satsa/crypto/src/stssymmetriccipher.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javaextensions/satsa/crypto/src/stssymmetriccipher.cpp Mon May 03 12:27:20 2010 +0300 @@ -0,0 +1,595 @@ +/* +* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: Implementation of STSSymmetricCipher + * +*/ + + + +#include "stssymmetriccipher.h" +#include +#include +#include +#include +#include +#include +#include "javajniutils.h" +#include "stsconstants.h" +#include "logger.h" + +namespace java +{ +namespace satsa +{ + + +STSSymmetricCipher::STSSymmetricCipher() +{ + mCipher = NULL; + mCipherCtx = NULL; + mCipherType = NULL; + +} + + +STSSymmetricCipher::~STSSymmetricCipher() +{ + // cleanup the cipher context + if (mCipherCtx != NULL) + { + EVP_CIPHER_CTX_cleanup(mCipherCtx); + mCipherCtx = NULL; + } + else + { + // do nothing + } +} + +STSSymmetricCipher* STSSymmetricCipher::Create( + STSTransformation* aTransformation, int* errCode) +{ + LOG(ESATSA, EInfo, "STSSymmetricCipher::Create+"); + STSSymmetricCipher* self = new STSSymmetricCipher; + if (self == NULL) + { + *errCode = KSTSErrNoMemory; + return NULL; + } + int retVal = self->Construct(aTransformation); + if (0 == retVal) + { + *errCode = retVal; + return self; + } + else + { + ELOG(ESATSA, "STSSymmetricCipher::Create failed"); + *errCode = retVal; + delete self; + self = 0; + return NULL; + } + +} + +int STSSymmetricCipher::Construct(STSTransformation* aTransformation) +{ + LOG(ESATSA, EInfo, "STSSymmetricCipher::Construct+"); + // load ciphers that are needed for lookup by openc apis + OpenSSL_add_all_ciphers(); + + // Check transformation validity. + + // Check mode + if (aTransformation->Mode() != STSModeCBC && aTransformation->Mode() + != STSModeECB && aTransformation->Mode() != STSModeNONE) + { + ELOG(ESATSA, "STSSymmetricCipher::Construct: mode is not supported"); + // Mode is not supported + return (KSTSErrNoSuchAlgorithm); + + } + + // Check padding + if (aTransformation->Padding() != STSPaddingNone + && aTransformation->Padding() != STSPaddingPKCS7 + && aTransformation->Padding() != STSPaddingPKCS5) + { + ELOG(ESATSA, "Construct: padding not supported"); + // Padding is not supported + return (KSTSErrNoSuchPadding); + + } + + // No errors, object constructed successfully + LOG(ESATSA, EInfo, "STSSymmetricCipher::Construct--"); + return 0; + +} + +jint STSSymmetricCipher::DoInit(JNIEnv* aJni, const TCipherMode aMode, + const jstring aKeyAlgorithm, const jstring aKeyFormat, + const jbyteArray aKeyEncoded, const jbyteArray aParams) +{ + LOG(ESATSA, EInfo, "STSSymmetricCipher::DoInit+"); + //Function return value + jint retVal = 0; + + // clear the old iv as the cipher is now being initialized/re-initialized + if (mIV != NULL) + { + delete mIV; + mIV = NULL; + } + miv_length = 0; + + //clear the old mKey + if (mKey != NULL) + { + delete mKey; + mKey = NULL; + } + mKey_length = 0; + + // Format: Only raw keys are supported + std::wstring key_format; + try + { + key_format = java::util::JniUtils::jstringToWstring(aJni, aKeyFormat); + } + catch (...) + { + ELOG(ESATSA, "DoInit: caught exception. Return error code"); + return (KSTSErrInvalidKey); + } + if (key_format != STSKeyFormatRAW) + { + ELOG(ESATSA, "DoInit:Not raw keys"); + return (KSTSErrInvalidKey); + } + + // Key must be created for used algorithm + std::wstring key_algorithm; + try + { + key_algorithm = java::util::JniUtils::jstringToWstring(aJni, + aKeyAlgorithm); + } + catch (...) + { + ELOG(ESATSA, "DoInit: caught exception. Return error code"); + return (KSTSErrInvalidKey); + } + if (mTransformation->Algorithm() != key_algorithm) + { + ELOG(ESATSA, "DoInit:Not proper algorithm"); + return (KSTSErrInvalidKey); + } + + // read the Key + //Create the native array from jbytearray + mKey_length = aJni->GetArrayLength(aKeyEncoded); + mKey = new unsigned char[mKey_length]; + aJni->GetByteArrayRegion(aKeyEncoded, 0, mKey_length, (signed char*) mKey); + + // Set the cipher type and Mode + if (mTransformation->Algorithm() == STSAlgorithmDES) + { + ELOG(ESATSA, "DoInit: DES algorithm"); + if (mTransformation->Mode() == STSModeCBC) + { + mCipherType = EVP_des_cbc(); + } + else if (mTransformation->Mode() == STSModeECB) + { + mCipherType = EVP_des_ecb(); + } + else if (mTransformation->Mode() == STSModeNONE) + { + mCipherType = EVP_des_cbc(); + } + + } + else if (mTransformation->Algorithm() == STSAlgorithm3DES) + { + LOG(ESATSA, EInfo, "STSSymmetricCipher::DoInit: 3DES algorithm"); + if (mTransformation->Mode() == STSModeCBC) + { + mCipherType = EVP_des_ede3_cbc(); + } + else if (mTransformation->Mode() == STSModeECB) + { + mCipherType = EVP_des_ede3_ecb(); + } + else if (mTransformation->Mode() == STSModeNONE) + { + mCipherType = EVP_des_ede3_cbc(); + } + + } + else if (mTransformation->Algorithm() == STSAlgorithmRC2) + { + LOG(ESATSA, EInfo, "STSSymmetricCipher::DoInit: RC2 algorothm"); + if (mTransformation->Mode() == STSModeCBC) + { + mCipherType = EVP_rc2_cbc(); + } + else if (mTransformation->Mode() == STSModeECB) + { + mCipherType = EVP_rc2_ecb(); + } + else if (mTransformation->Mode() == STSModeNONE) + { + mCipherType = EVP_rc2_cbc(); + } + + } + else if (mTransformation->Algorithm() == STSAlgorithmAES) + { + LOG(ESATSA, EInfo, "STSSymmetricCipher::DoInit: AES algorithm"); + // AES algorithms based on key size + switch (mKey_length) + { + case 16: + if (mTransformation->Mode() == STSModeCBC) + { + mCipherType = EVP_aes_128_cbc(); + } + else if (mTransformation->Mode() == STSModeECB) + { + mCipherType = EVP_aes_128_ecb(); + } + else if (mTransformation->Mode() == STSModeNONE) + { + mCipherType = EVP_aes_128_cbc(); + } + break; + case 24: + if (mTransformation->Mode() == STSModeCBC) + { + mCipherType = EVP_aes_192_cbc(); + } + else if (mTransformation->Mode() == STSModeECB) + { + mCipherType = EVP_aes_192_ecb(); + } + else if (mTransformation->Mode() == STSModeNONE) + { + mCipherType = EVP_aes_192_cbc(); + } + break; + case 32: + if (mTransformation->Mode() == STSModeCBC) + { + mCipherType = EVP_aes_256_cbc(); + } + else if (mTransformation->Mode() == STSModeECB) + { + mCipherType = EVP_aes_256_ecb(); + } + else if (mTransformation->Mode() == STSModeNONE) + { + mCipherType = EVP_aes_256_cbc(); + } + break; + default: + // Invalid Key Size + ELOG(ESATSA, "DoInit:Invalid Key Size"); + return (KSTSErrInvalidKey); + + }; + } + + // cipher type could not be determined + if (mCipherType == NULL) + { + ELOG(ESATSA, "DoInit:cipher type could not be determined"); + return (KSTSErrNoSuchAlgorithm); + } + // determined cipher type, determine and validate the parameters + // Block size is needed for padding and iv + int blocksize = EVP_CIPHER_block_size(mCipherType); + + // Check parameters, create IV for algorithm. + if (mTransformation->Mode() == STSModeECB) + { + // ECB mode may not have IV + if (aParams) + { + ELOG(ESATSA, "DoInit:ECB mode may not have IV"); + return (KSTSErrArgument); + } + } + else + { + // CBC mode must have IV + if (aParams) + { + // IV was given as a parameter + // Create the native array from jbytearray + miv_length = aJni->GetArrayLength(aParams); + mIV = new unsigned char[miv_length]; + aJni->GetByteArrayRegion(aParams, 0, miv_length, (signed char*) mIV); + + // IV's size must be same as block size. + if (blocksize != miv_length) + { + ELOG(ESATSA, "DoInit:blocksize mismatch"); + return (KSTSErrArgument); + } + + } + else + { + if (aMode == EDecryptMode) + { + ELOG(ESATSA, "DoInit: In decrypt mode IV is needed."); + // In encrypt mode IV is needed. + return (KSTSErrArgument); + } + + // IV was not given, generate new random iv same as blocksize + miv_length = blocksize; + mIV = new unsigned char[miv_length]; + if (mIV) + { + RAND_pseudo_bytes(mIV, miv_length); + } + else + { + mIV = NULL; + miv_length = 0; + ELOG(ESATSA, "DoInit:IV is not proper"); + return KSTSErrNoMemory; + } + + } + + } + + // Initialize the cipher context + mCipherCtx = new EVP_CIPHER_CTX; + if (mCipherCtx == NULL) + { + ELOG(ESATSA, "DoInit:cipher context is null"); + // cipher context could not be allocated + return (KSTSErrNoMemory); + } + // Initialize the cipher context + EVP_CIPHER_CTX_init(mCipherCtx); + + // Create decryptor or encryptor according to mode + mMode = aMode; + int ret; + if (mMode == EEncryptMode) + { + ret = EVP_CipherInit_ex(mCipherCtx, mCipherType, NULL, mKey, mIV, 1); + if (0 == ret) + { + ELOG(ESATSA, "DoInit:cipher:encrypt:cipher could not be initialized"); + retVal = KSTSErrNoMemory; + } + } + else + { + // Otherwise EDecryptMode + ret = EVP_CipherInit_ex(mCipherCtx, mCipherType, NULL, mKey, mIV, 0); + if (0 == ret) + { + ELOG(ESATSA, "DoInit:cipher:decrypt:cipher could not be initialized"); + retVal = KSTSErrNoMemory; + } + } + + // Set the padding + if (mTransformation->Padding() == STSPaddingPKCS7 + || mTransformation->Padding() == STSPaddingPKCS5) + { + LOG(ESATSA, EInfo, "Padding for pkcs7 or pkcs5"); + EVP_CIPHER_CTX_set_padding(mCipherCtx, 1); // always returns 1 (Success) + } + else + { + EVP_CIPHER_CTX_set_padding(mCipherCtx, 0); // always returns 1 (Success) + } + + // reset state. + mBytesProcessed = 0; + //return + LOG(ESATSA, EInfo, "STSSymmetricCipher::DoInit--"); + return retVal; +} + +jint STSSymmetricCipher::DoFinal(JNIEnv* aJni, jbyteArray aInput, + jint aInputOffset, jint aInputLength, jbyteArray aOutput, + jint aOutputOffset) +{ + LOG(ESATSA, EInfo, "STSSymmetricCipher::DoFinal+"); + // Variable to hold the error code + int retVal = 0; + + if (mCipherCtx == NULL || mCipherType == NULL) + { + ELOG(ESATSA, "Cipher context/type not ready"); + // Init was not called successfully. + return (KSTSErrIllegalState); + } + + int blocksize = EVP_CIPHER_block_size(mCipherType); + int bytesProcessed = aInputLength + mBytesProcessed; + // reset state. + mBytesProcessed = 0; + if ((bytesProcessed % blocksize != 0) && ((mMode == EDecryptMode) + || (mTransformation->Padding() == STSPaddingNone))) + { + ELOG(ESATSA, "Processing failed"); + return (KSTSErrIllegalBlockSize); + } + + // read the input data + unsigned char* inputBuf = new unsigned char[aInputLength]; + aJni->GetByteArrayRegion(aInput, aInputOffset, aInputLength, + (signed char *) inputBuf); + + // create the output native array to hold the output + int max_output_length = aInputLength + blocksize - 1; + unsigned char* outputBuf = new unsigned char[max_output_length]; + int outputLength = 0; + + // if inputlength is > 0 then there is some input data to update + if (aInputLength > 0) + { + // call update + retVal = EVP_CipherUpdate(mCipherCtx, outputBuf, &outputLength, + inputBuf, aInputLength); + if (0 == retVal) + { + ELOG(ESATSA, "Update OpenC API failed"); + // openc api returned failure + return KSTSErrBadPadding; + } + } + + // finalize the output + int final_length = 0; + retVal = EVP_CipherFinal_ex(mCipherCtx, outputBuf + outputLength, + &final_length); + if (0 == retVal) + { + ELOG(ESATSA, "Final OpenC API failed"); + // openc api returned failure + return KSTSErrBadPadding; + } + + // Re-initialize the cipher back to its init state + int ret = EVP_CIPHER_CTX_cleanup(mCipherCtx); + if (0 == ret) + { + ELOG(ESATSA, "Cleanup OpenC API failed"); + retVal = KSTSErrIllegalState; + } + // Initialize the cipher context + EVP_CIPHER_CTX_init(mCipherCtx); + + // Create decryptor or encryptor according to mode + if (mMode == EEncryptMode) + { + int ret = EVP_CipherInit_ex(mCipherCtx, mCipherType, NULL, mKey, mIV, 1); + if (0 == ret) + { + ELOG(ESATSA, "encrypt mode:cipher init failed"); + retVal = KSTSErrNoMemory; + } + } + else + { + // Otherwise EDecryptMode + int ret = EVP_CipherInit_ex(mCipherCtx, mCipherType, NULL, mKey, mIV, 0); + if (0 == ret) + { + retVal = KSTSErrNoMemory; + } + } + + // Set the padding + if (mTransformation->Padding() == STSPaddingPKCS7 + || mTransformation->Padding() == STSPaddingPKCS5) + { + EVP_CIPHER_CTX_set_padding(mCipherCtx, 1); // always returns 1(Success) + } + else + { + EVP_CIPHER_CTX_set_padding(mCipherCtx, 0); // always returns 1(Success) + } + + if ((outputLength + final_length) > (aJni->GetArrayLength(aOutput) + - aOutputOffset)) + { + // Output buffer cannot hold the output of the cipher + return (KSTSErrShortBuffer); + } + + aJni->SetByteArrayRegion(aOutput, aOutputOffset, (outputLength + + final_length), (signed char *) outputBuf); + retVal = outputLength + final_length; + + LOG(ESATSA, EInfo, "STSSymmetricCipher::DoFinal--"); + return retVal; + +} + +jint STSSymmetricCipher::Update(JNIEnv* aJni, jbyteArray aInput, + jint aInputOffset, jint aInputLength, jbyteArray aOutput, + jint aOutputOffset) +{ + LOG(ESATSA, EInfo, "STSSymmetricCipher::Update+"); + // Variable to hold the error code + int retVal = 0; + + if (mCipherCtx == NULL || mCipherType == NULL) + { + ELOG(ESATSA, "Init was not called successfully"); + // Init was not called successfully. + return (KSTSErrIllegalState); + } + else if (0 == aInputLength) + { + // if inputlength is 0 then there is nothing to update + ELOG(ESATSA, "Inputlength 0, nothing to update"); + // return 0 + return retVal; + } + + // read the input data + unsigned char* inputBuf = new unsigned char[aInputLength]; + aJni->GetByteArrayRegion(aInput, aInputOffset, aInputLength, + (signed char *) inputBuf); + + // create the output native array to hold the output + int blocksize = EVP_CIPHER_block_size(mCipherType); + int max_output_length = aInputLength + blocksize - 1; + unsigned char* outputBuf = new unsigned char[max_output_length]; + int outputLength = 0; + + // call the update function + retVal = EVP_CipherUpdate(mCipherCtx, outputBuf, &outputLength, inputBuf, + aInputLength); + if (0 == retVal) + { + ELOG(ESATSA, "Update OpenC API failed"); + // openc api returned failure + return KSTSErrBadPadding; + } + else if ((outputLength) > (aJni->GetArrayLength(aOutput) - aOutputOffset)) + { + ELOG(ESATSA, "Update: Short buffer"); + return (KSTSErrShortBuffer); + } + + aJni->SetByteArrayRegion(aOutput, aOutputOffset, outputLength, + (signed char *) outputBuf); + + mBytesProcessed += aInputLength; + + // return the number of bytes processed. + retVal = outputLength; + LOG(ESATSA, EInfo, "STSSymmetricCipher::Update--"); + return retVal; + +} +} // namespace satsa +} // namespace java + +