javaextensions/satsa/crypto/src/stssymmetriccipher.cpp
branchRCL_3
changeset 14 04becd199f91
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javaextensions/satsa/crypto/src/stssymmetriccipher.cpp	Tue Apr 27 16:30:29 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 <openssl/rand.h>
+#include <msymmetriccipher.h>
+#include <bufferedtransformation.h>
+#include <cbcmode.h>
+#include <padding.h>
+#include <random.h>
+#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
+
+