CK_RV PKCS11_Encryption_OpenSSL::DecryptUpdate(Cryptoki_Session_Context* pSessionCtx, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen)
{
    OPENSSL_HEADER();

    OpenSSLEncryptData *pDec;
    
    if(pSessionCtx == NULL || pSessionCtx->DecryptionCtx == NULL) return CKR_SESSION_CLOSED;

    pDec = (OpenSSLEncryptData*)pSessionCtx->DecryptionCtx;

    if(pDec->IsSymmetric)
    {
        int outLen = *pulPartLen;
        
        OPENSSL_CHECKRESULT(EVP_DecryptUpdate((EVP_CIPHER_CTX*)pDec->Key->ctx, pPart, &outLen, pEncryptedPart, ulEncryptedPartLen));
        
        *pulPartLen = outLen;
    }
    else
    {
        size_t outLen = *pulPartLen;
        
        OPENSSL_CHECKRESULT(EVP_PKEY_decrypt((EVP_PKEY_CTX*)pDec->Key->ctx, pPart, &outLen, pEncryptedPart, ulEncryptedPartLen));

        *pulPartLen = outLen;
    }

    OPENSSL_CLEANUP();

    if(retVal != CKR_OK)
    {
        TINYCLR_SSL_FREE(pDec);
        pSessionCtx->DecryptionCtx = NULL;
    }

    OPENSSL_RETURN();
}    
CK_RV PKCS11_Encryption_OpenSSL::InitHelper(Cryptoki_Session_Context* pSessionCtx, CK_MECHANISM_PTR pEncryptMech, CK_OBJECT_HANDLE hKey, BOOL isEncrypt)
{
    OPENSSL_HEADER();
    
    OpenSSLEncryptData* pEnc;
    const EVP_CIPHER* pCipher;
    int padding = 0;

    if(             pSessionCtx                 == NULL) return CKR_SESSION_CLOSED;
    if( isEncrypt && pSessionCtx->EncryptionCtx != NULL) return CKR_SESSION_PARALLEL_NOT_SUPPORTED;
    if(!isEncrypt && pSessionCtx->DecryptionCtx != NULL) return CKR_SESSION_PARALLEL_NOT_SUPPORTED;
    
    pEnc = (OpenSSLEncryptData*)TINYCLR_SSL_MALLOC(sizeof(*pEnc));

    if(pEnc == NULL) return CKR_DEVICE_MEMORY;

    TINYCLR_SSL_MEMSET(pEnc, 0, sizeof(*pEnc));

    pEnc->Key = PKCS11_Keys_OpenSSL::GetKeyFromHandle(pSessionCtx, hKey, !isEncrypt);

    pEnc->IsSymmetric = TRUE;

    switch(pEncryptMech->mechanism)
    {
        case CKM_AES_CBC:
        case CKM_AES_CBC_PAD:
            switch(pEnc->Key->size)
            {
                case 128:
                    pCipher = EVP_aes_128_cbc();
                    break;
                case 192:
                    pCipher = EVP_aes_192_cbc();
                    break;
                case 256:
                    pCipher = EVP_aes_256_cbc();
                    break;
                default:
                    OPENSSL_SET_AND_LEAVE(CKR_MECHANISM_INVALID);
            }
            if(pEncryptMech->mechanism == CKM_AES_CBC_PAD)
            {
                padding = 1;
            }
            break;

        case CKM_AES_ECB:
        case CKM_AES_ECB_PAD:
            switch(pEnc->Key->size)
            {
                case 128:
                    pCipher = EVP_aes_128_ecb();
                    break;
                case 192:
                    pCipher = EVP_aes_192_ecb();
                    break;
                case 256:
                    pCipher = EVP_aes_256_ecb();
                    break;
                default:
                    OPENSSL_SET_AND_LEAVE(CKR_MECHANISM_INVALID);
            }
            if(pEncryptMech->mechanism == CKM_AES_ECB_PAD)
            {
                padding = 1;
            }
            break;

        case CKM_DES3_CBC:
            pCipher = EVP_des_ede3_cbc();
            break;

        case CKM_DES3_CBC_PAD:
            pCipher = EVP_des_ede3_cbc();
            padding = 1;
            break;

        case CKM_RSA_PKCS:
            pEnc->IsSymmetric= FALSE;
            padding = RSA_PKCS1_PADDING;
            break;

        default:
            OPENSSL_SET_AND_LEAVE(CKR_MECHANISM_INVALID);
    }

    if(pEnc->IsSymmetric)
    {
        if(pEncryptMech->ulParameterLen > 0 && pEncryptMech->ulParameterLen > 0)
        {
            memcpy(pEnc->IV, pEncryptMech->pParameter, pEncryptMech->ulParameterLen);
        }

        pEnc->Key->ctx = &pEnc->SymmetricCtx;

        if(isEncrypt) 
        {
            OPENSSL_CHECKRESULT(EVP_EncryptInit(&pEnc->SymmetricCtx, pCipher, (const UINT8*)pEnc->Key->key, pEnc->IV));
        }
        else
        {
            OPENSSL_CHECKRESULT(EVP_DecryptInit(&pEnc->SymmetricCtx, pCipher, (const UINT8*)pEnc->Key->key, pEnc->IV));
        }

        OPENSSL_CHECKRESULT(EVP_CIPHER_CTX_set_padding(&pEnc->SymmetricCtx, padding));
    }
    else
    {
        pEnc->Key->ctx = EVP_PKEY_CTX_new((EVP_PKEY*)pEnc->Key->key, NULL);

        if(isEncrypt)
        {
            OPENSSL_CHECKRESULT(EVP_PKEY_encrypt_init       ((EVP_PKEY_CTX*)pEnc->Key->ctx         ));
            OPENSSL_CHECKRESULT(EVP_PKEY_CTX_set_rsa_padding((EVP_PKEY_CTX*)pEnc->Key->ctx, padding));
        }
        else
        {
            OPENSSL_CHECKRESULT(EVP_PKEY_decrypt_init       ((EVP_PKEY_CTX*)pEnc->Key->ctx         ));
            OPENSSL_CHECKRESULT(EVP_PKEY_CTX_set_rsa_padding((EVP_PKEY_CTX*)pEnc->Key->ctx, padding));
        }
    }

    if(isEncrypt) pSessionCtx->EncryptionCtx = pEnc;
    else          pSessionCtx->DecryptionCtx = pEnc;

    OPENSSL_CLEANUP();
    if(retVal != CKR_OK && pEnc != NULL)
    {
        TINYCLR_SSL_FREE(pEnc);
    }
    OPENSSL_RETURN();
    
}
CK_RV PKCS11_Objects_OpenSSL::SetAttributeValue(Cryptoki_Session_Context* pSessionCtx, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) 
{
    OBJECT_DATA* pObj = PKCS11_Objects_OpenSSL::GetObjectFromHandle(pSessionCtx, hObject);

    if(pObj == NULL) return CKR_OBJECT_HANDLE_INVALID;

    if(pObj->Type == CertificateType)
    {
        CERT_DATA* pCertData = (CERT_DATA*)pObj->Data;
        X509* pCert = pCertData->cert;
        CHAR group[20] = {0};
        CHAR fileName[20] = {0};
        INT32 len = 0;

        BOOL fSave = FALSE;
        BOOL fDelete = FALSE;
    
        for(int i=0; i<(int)ulCount; i++)
        {
            switch(pTemplate[i].type)
            {
                case CKA_PERSIST:
                    fSave = SwapEndianIfBEc32(*(INT32*)pTemplate[i].pValue) != 0;
                    fDelete = !fSave;
                    break;

                case CKA_LABEL:
                    len = pTemplate[i].ulValueLen;

                    if(len >= ARRAYSIZE(group))
                    {
                        len = ARRAYSIZE(group) - 1;
                    }
                    
                    TINYCLR_SSL_MEMCPY(group, pTemplate[i].pValue, len);
                    
                    group[len] = 0;
                    
                    break;

                case CKA_OBJECT_ID:
                    len = pTemplate[i].ulValueLen;

                    if(len >= ARRAYSIZE(fileName))
                    {
                        len = ARRAYSIZE(fileName) - 1;
                    }
                    
                    TINYCLR_SSL_MEMCPY(fileName, pTemplate[i].pValue, len);
                    
                    fileName[len] = 0;
                    
                    break;

                default:
                    return CKR_ATTRIBUTE_TYPE_INVALID;
            }
        }

        if(fDelete)
        {
            hal_strcpy_s(fileName, ARRAYSIZE(pObj->FileName), pObj->FileName);

            pObj->GroupName[0] = 0;
                
            if(g_OpenSSL_Token.Storage != NULL && g_OpenSSL_Token.Storage->Delete != NULL)
            {
                if(!g_OpenSSL_Token.Storage->Delete(fileName, group)) return CKR_FUNCTION_FAILED;
            }
            else
            {
                return CKR_FUNCTION_NOT_SUPPORTED;
            }
        }
        else if(fSave)
        {
            hal_strcpy_s(pObj->GroupName, ARRAYSIZE(pObj->GroupName), group   );
            hal_strcpy_s(pObj->FileName , ARRAYSIZE(pObj->FileName ), fileName);
            
            if(g_OpenSSL_Token.Storage != NULL && g_OpenSSL_Token.Storage->Create != NULL)
            {
                BOOL res;
                UINT8* pData, *pTmp;
                INT32 len = i2d_X509( pCert, NULL );

                if(len <= 0) return CKR_FUNCTION_FAILED;

                pData = (UINT8*)TINYCLR_SSL_MALLOC(len);

                if(pData == NULL) return CKR_DEVICE_MEMORY;

                pTmp = pData;

                i2d_X509(pCert, &pTmp);

                // TODO: Save private key as well
                
                res = g_OpenSSL_Token.Storage->Create( fileName, group, CertificateType, pData, len );

                TINYCLR_SSL_FREE(pData);

                if(!res) return CKR_FUNCTION_FAILED;
            }
            else
            {
                return CKR_FUNCTION_NOT_SUPPORTED;
            }
        }
        else
        {
            return CKR_ATTRIBUTE_TYPE_INVALID;
        }
    }

    return CKR_OK;
}
CK_RV PKCS11_Objects_OpenSSL::FindObjects(Cryptoki_Session_Context* pSessionCtx, CK_OBJECT_HANDLE_PTR phObjects, CK_ULONG ulMaxCount, CK_ULONG_PTR pulObjectCount)
{
    FIND_OBJECT_DATA *pData = (FIND_OBJECT_DATA*)pSessionCtx->FindObjectsCtx;
    UINT32 cntObj = 0;

    *pulObjectCount = 0;
    
    if(pData == NULL) return CKR_FUNCTION_FAILED;
    if(ulMaxCount == 0) return CKR_ARGUMENTS_BAD;

    {
        FileEnumCtx ctx;
        CHAR fileName[20];
        
        if(g_OpenSSL_Token.Storage == NULL || g_OpenSSL_Token.Storage->GetFileEnum == NULL)
        {
            return CKR_FUNCTION_NOT_SUPPORTED;
        }

        if(!g_OpenSSL_Token.Storage->GetFileEnum(pData->GroupName, pData->ObjectType, ctx)) return CKR_FUNCTION_FAILED;

        while(g_OpenSSL_Token.Storage->GetNextFile(ctx, fileName, ARRAYSIZE(fileName)))
        {
            UINT32 dataLen = 0;
            
            if(g_OpenSSL_Token.Storage->Read(fileName, pData->GroupName, pData->ObjectType, NULL, dataLen))
            {
                UINT8* pObj = (UINT8*)TINYCLR_SSL_MALLOC(dataLen);

                if(pObj)
                {
                    g_OpenSSL_Token.Storage->Read(fileName, pData->GroupName, pData->ObjectType, pObj, dataLen);
                    
                    switch(pData->ObjectType)
                    {
                        case CertificateType:
                            {
                                OBJECT_DATA *pObject = NULL;
                                const UINT8* pTmp = pObj;
                                CK_OBJECT_HANDLE hObjTmp, *pObjHandle;

                                X509* x509 = d2i_X509(NULL, &pTmp, dataLen);

                                if(x509 == NULL) 
                                {
                                    TINYCLR_SSL_FREE(pObj);    
                                    return CKR_FUNCTION_FAILED;
                                }

                                if(phObjects == NULL)
                                {
                                    pObjHandle = &hObjTmp;
                                }
                                else
                                {
                                    pObjHandle = &phObjects[cntObj];
                                }                                

                                if(CKR_OK != LoadX509Cert(pSessionCtx, x509, &pObject, NULL, pObjHandle))
                                {
                                    X509_free(x509);
                                    TINYCLR_SSL_FREE(pObj);
                                    //return CKR_FUNCTION_FAILED;

                                    g_OpenSSL_Token.Storage->Delete( fileName, pData->GroupName );
                                    continue;
                                }

                                hal_strcpy_s(pObject->GroupName, ARRAYSIZE(pObject->GroupName), pData->GroupName);
                                hal_strcpy_s(pObject->FileName , ARRAYSIZE(pObject->FileName ), fileName );
                                cntObj++;

                                TINYCLR_SSL_FREE(pObj);

                                if(cntObj >= ulMaxCount)
                                {
                                    *pulObjectCount = cntObj;
                                    return CKR_OK;
                                }
                            }
                            break;

                        case KeyType:
                            {
                                // TODO:
                                TINYCLR_SSL_FREE(pObj);
                            }
                            break;

                        default:
                            // TODO:
                            TINYCLR_SSL_FREE(pObj);
                            break;
                    }
                }
            }
        }
    }

    *pulObjectCount = cntObj;

    return CKR_OK;
}
CK_RV PKCS11_Digest_OpenSSL::DigestInit(Cryptoki_Session_Context* pSessionCtx, CK_MECHANISM_PTR pMechanism)
{
    OPENSSL_HEADER();
    
    OpenSSLDigestData* pDigData;
    const EVP_MD*      pDigest;
    CK_OBJECT_HANDLE   hKey   = CK_OBJECT_HANDLE_INVALID;
    bool               isHMAC = false;

    if(pSessionCtx            == NULL) return CKR_SESSION_CLOSED;
    if(pSessionCtx->DigestCtx != NULL) return CKR_SESSION_PARALLEL_NOT_SUPPORTED; // another digest is in progress
    
    pDigData = (OpenSSLDigestData*)TINYCLR_SSL_MALLOC(sizeof(*pDigData));

    if(pDigData == NULL) return CKR_DEVICE_MEMORY;

    TINYCLR_SSL_MEMSET(pDigData, 0, sizeof(*pDigData));
    
    EVP_MD_CTX_init(&pDigData->CurrentCtx);
    
    switch(pMechanism->mechanism)
    {
        case CKM_SHA_1:
            pDigest = EVP_sha1();
            break;
        case CKM_SHA224:
            pDigest = EVP_sha224();
            break;
        case CKM_SHA256:
            pDigest = EVP_sha256();
            break;
        case CKM_SHA384:
            pDigest = EVP_sha384();
            break;
        case CKM_SHA512:
            pDigest = EVP_sha512();
            break;

        case CKM_MD5:
            pDigest = EVP_md5();
            break;

        case CKM_RIPEMD160:
            pDigest = EVP_ripemd160();
            break;

        case CKM_MD5_HMAC:
            pDigest = EVP_md5();
            isHMAC = true;
            break;

        case CKM_SHA_1_HMAC:
            pDigest = EVP_sha1();
            isHMAC = true;
            break;

        case CKM_SHA256_HMAC:
            pDigest = EVP_sha256();
            isHMAC = true;
            break;

        case CKM_SHA384_HMAC:
            pDigest = EVP_sha384();
            isHMAC = true;
            break;

        case CKM_SHA512_HMAC:
            pDigest = EVP_sha512();
            isHMAC = true;
            break;

        case CKM_RIPEMD160_HMAC:
            pDigest = EVP_ripemd160();
            isHMAC = true;
            break;
            

        default:
            OPENSSL_SET_AND_LEAVE(CKR_MECHANISM_INVALID);
    }


    if(isHMAC)
    {
        if(pMechanism->pParameter != NULL && pMechanism->ulParameterLen == sizeof(CK_OBJECT_HANDLE))
        {
            hKey = SwapEndianIfBEc32(*(CK_OBJECT_HANDLE*)pMechanism->pParameter);
        }
        else 
        {
            OPENSSL_SET_AND_LEAVE(CKR_MECHANISM_PARAM_INVALID);
        }

        pDigData->HmacKey = PKCS11_Keys_OpenSSL::GetKeyFromHandle(pSessionCtx, hKey, TRUE);

        if(pDigData->HmacKey==NULL) OPENSSL_SET_AND_LEAVE(CKR_MECHANISM_PARAM_INVALID);

        pDigData->HmacCtx.md = pDigest;

        OPENSSL_CHECKRESULT(HMAC_Init(&pDigData->HmacCtx, pDigData->HmacKey->key, pDigData->HmacKey->size/8, pDigData->HmacCtx.md));
    }
    else
    {
        OPENSSL_CHECKRESULT(EVP_DigestInit_ex(&pDigData->CurrentCtx, pDigest, NULL));
    }
    
    pSessionCtx->DigestCtx = pDigData;
    
    OPENSSL_CLEANUP();
    if(retVal != CKR_OK && pDigData != NULL)
    {
        TINYCLR_SSL_FREE(pDigData);
    }
    OPENSSL_RETURN();
}