/*
* Copyright (c) 2002-2009 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:  Certificate management methods
*
*/



// INCLUDE FILES
#include    "Wimi.h"            // wimi definitions
#include    "WimCertHandler.h"
#include    "WimMemMgmt.h"
#include    "WimSession.h"
#include    "WimResponse.h"
#include    "WimUtilityFuncs.h"
#include    <asn1enc.h>         // asn.1 encoding
#include    <asn1dec.h>         // asn.1 decoding
#include    <x509cert.h>        // cx509certificate
#ifndef SYMBIAN_ENABLE_SPLIT_HEADERS
#include    <x509keys.h>        // cx509rsapublickey
#else
#include    <x509keys.h>        // cx509rsapublickey
#include    <x509keyencoder.h>        // cx509rsapublickey
#endif
#include    <wtlscert.h>        // cwtlscertificate
#include    <wtlskeys.h>        // cwtlsrsapublickey
#include    "WimTrace.h"
#include    "WimCallbackImpl.h"
#include    "WimCleanup.h"

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CWimCertHandler::CWimCertHandler
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CWimCertHandler::CWimCertHandler()
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::CWimCertHandler | Begin"));
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::ConstructL()
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::ConstructL | Begin"));
    iWimUtilFuncs = CWimUtilityFuncs::NewL();
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CWimCertHandler* CWimCertHandler::NewL() 
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::NewL | Begin"));
    CWimCertHandler* self = new( ELeave ) CWimCertHandler;
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

    
// Destructor
CWimCertHandler::~CWimCertHandler()
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::~CWimCertHandler | Begin"));
    delete iWimUtilFuncs;

    for( TInt index = 0; index < iCertRefLst.Count(); ++index )
        {
        WIMI_Ref_t* ref = reinterpret_cast< WIMI_Ref_t* >( iCertRefLst[ index ] );
        _WIMTRACE2(_L("WIM | WIMServer | CWimCertHandler::~CWimCertHandler, -ref 0x%08x"), ref);
        free_WIMI_Ref_t( ref );
        iCertRefLst[ index ] = 0;
        }
    iCertRefLst.Reset();
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetCertificatesFromWimL
// Fetches certificates from WIM card.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::GetCertificatesFromWimL(
    const RMessage2& aMessage, 
    CWimMemMgmt* aWimMgmt )
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::GetCertificatesFromWimL | Begin"));
    WIMI_STAT callStatus = WIMI_Ok;
    TUint8 certNum = 0;
    WIMI_Ref_t* temp = NULL;

    HBufC8* certRefLst = iWimUtilFuncs->DesLC( 0, aMessage );
    HBufC8* certInfoLst = iWimUtilFuncs->DesLC( 1, aMessage );

    TWimEntryType certEntryType = ( TWimEntryType ) aMessage.Int2();

    TUint32* pCertRefLst = ( TUint32* )( certRefLst->Des().Ptr() );
    TWimCertInfo* pCertInfoLst = ( TWimCertInfo* )( certInfoLst->Des().Ptr() );

    temp = aWimMgmt->WimRef();
    if ( !temp )
        { 
        temp = WIMI_GetWIMRef( 0 );
        aWimMgmt->SetWIMRef( temp );    // takes ownership
        }

    if ( temp )
        {
        if ( EWimEntryTypeAll == certEntryType || 
             EWimEntryTypeCA == certEntryType )
            {
            callStatus = GetCertificateFromWimRefL( temp, WIMI_CU_CA,
                                                    certNum, pCertRefLst,
                                                    pCertInfoLst, aMessage );

            }
        if ( callStatus == WIMI_Ok && ( EWimEntryTypeAll == certEntryType || 
                                   EWimEntryTypePersonal == certEntryType ) )
            {
            callStatus = GetCertificateFromWimRefL( temp, WIMI_CU_Client,
                                                    certNum, pCertRefLst,
                                                    pCertInfoLst, aMessage );

            }
        
        //record the ref for sanity checking, deallocate old refs first
        for( TInt index = 0; index < iCertRefLst.Count(); ++index )
            {
            WIMI_Ref_t* ref = reinterpret_cast< WIMI_Ref_t* >( iCertRefLst[ index ] );
            _WIMTRACE2(_L("WIM | WIMServer | CWimCertHandler::GetCertificatesFromWimL, -ref 0x%08x"), ref);
            free_WIMI_Ref_t( ref );
            iCertRefLst[ index ] = 0;
            }
        iCertRefLst.Reset();
        for( TInt index = 0; index < certNum; ++index )
            {
            _WIMTRACE2(_L("WIM | WIMServer | CWimCertHandler::GetCertificatesFromWimL, +ref 0x%08x"), pCertRefLst[ index ]);
            iCertRefLst.AppendL( pCertRefLst[ index ] );
            }
        
        aMessage.WriteL( 0, certRefLst->Des() );
        aMessage.WriteL( 1, certInfoLst->Des() );
        }
    else
        {
        callStatus = WIMI_ERR_OutOfMemory;
        }
    CleanupStack::PopAndDestroy( 2, certRefLst );   // certInfoLst, certRefLst
    aMessage.Complete( CWimUtilityFuncs::MapWIMError( callStatus ) );
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetCertificateFromWimRefL
// Fetches certificate from the WIM card.
// -----------------------------------------------------------------------------
//
WIMI_STAT CWimCertHandler::GetCertificateFromWimRefL(
    WIMI_Ref_t* aTmpWimRef,
    TInt8 aUsage,
    TUint8& aCertNum,
    TUint32* aCertRefLst,
    TWimCertInfo* aCertInfoLst,
    const RMessage2& aMessage )
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::GetCertificateFromWimRefL | Begin"));
    TUint8 tempCrtCount;
    WIMI_RefList_t refList = NULL;
    WIMI_STAT callStatus = WIMI_Ok;

    if ( aTmpWimRef )
        {
        callStatus = WIMI_GetCertificateListByWIM( aTmpWimRef, 
                                                   aUsage, 
                                                   &tempCrtCount, 
                                                   &refList );

        if ( callStatus == WIMI_Ok )
            {
            // If the certifcate length is 0,
            // the step increases by 1.
            TInt step = 0;
            TInt err = KErrNone;
            for ( TUint8 certIndex = 0; certIndex < tempCrtCount; certIndex++ )
                {
                TInt current = aCertNum + certIndex - step;
                err = CopyCertificateInfo( aCertInfoLst[current], refList[certIndex], aMessage );

                if ( err == KErrNone )
                    {
                    // transfers ownership of refList item to aCertRefLst
                    aCertRefLst[current] = reinterpret_cast< TUint32 >( refList[certIndex] );
                    refList[certIndex] = NULL;
                    }
                else // KErrArgument
                    {
                    // ingore certificate info and continue with the next
                	step++;
                    free_WIMI_Ref_t( refList[certIndex] );
                    }
                }
            //variable step is equal to the number of certificate in CDF whose 
            //length is set as 0.     
            aCertNum = static_cast< TUint8 >( aCertNum + tempCrtCount - step );

            // Because list items are moved to aCertRefLst, only refList array
            // needs to be freed. Cannot use free_WIMI_RefList_t() as it would
            // delete also items contained in refList.
            WSL_OS_Free( refList );
            }
        }

    return callStatus;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::CopyCertificateInfo
// Copies certificate information to client's allocated memory area.
// This function MAY NOT leave.
// -----------------------------------------------------------------------------
//
TInt CWimCertHandler::CopyCertificateInfo(
    TWimCertInfo& aCertInfo,
    WIMI_Ref_t* aCert,
    const RMessage2& /*aMessage*/ )
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::CopyCertificateInfo | Begin"));
    WIMI_Ref_t* tempRef = NULL;
    WIMI_BinData_t ptLabel;
    WIMI_BinData_t ptKeyID;
    WIMI_BinData_t ptCAID;
    WIMI_BinData_t ptIssuerHash;
    WIMI_BinData_t ptTrustedUsage;
    TUint8 uiCDFRefs;
    TUint8 usage;
    TUint8 type;
    TUint16 certLen;
    TUint8 modifiable = 0;
    WIMI_STAT callStatus = WIMI_GetCertificateInfo( 
                                aCert,
                                &tempRef,
                                &ptLabel,
                                &ptKeyID, /* Key Id (hash)*/
                                &ptCAID,
                                &ptIssuerHash,
                                &ptTrustedUsage,
                                &uiCDFRefs,
                                &usage,  /* 0 = client, 1 = CA */
                                &type,   /* WTLSCert(1), 
                                            X509Cert(2), 
                                            X968Cert(3), 
                                            CertURL(4) */ 
                                &certLen,   /* cert. content or URL length */
                                &modifiable );

    if ( callStatus == WIMI_Ok )
        {
        free_WIMI_Ref_t( tempRef );

        // To make sure the length of the certificate is not zero
        _WIMTRACE2(_L("WIM | WIMServer | CWimCertHandler::CopyCertificateInfoL | certLen %d"), certLen);
        if ( certLen == 0 )
            {
            WSL_OS_Free( ptLabel.pb_buf );
            WSL_OS_Free( ptKeyID.pb_buf );
            WSL_OS_Free( ptCAID.pb_buf );
            WSL_OS_Free( ptIssuerHash.pb_buf );
            WSL_OS_Free( ptTrustedUsage.pb_buf );
        	return KErrArgument;
            }

        // it is x509cert
        if ( type == 2 && certLen != 0 )
            {
            //use this rough sanity checking for temp
            if ( certLen < 10 )
                {
                _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::CopyCertificateInfoL | cert is malformated, return KErrArgument"));
                WSL_OS_Free( ptLabel.pb_buf );
                WSL_OS_Free( ptKeyID.pb_buf );
                WSL_OS_Free( ptCAID.pb_buf );
                WSL_OS_Free( ptIssuerHash.pb_buf );
                WSL_OS_Free( ptTrustedUsage.pb_buf );
                return KErrArgument;
                }
            }
        _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::CopyCertificateInfoL | X509 check ok"));

        aCertInfo.iLabel.Copy(
            TPtr8(
                ptLabel.pb_buf, 
                ptLabel.ui_buf_length, 
                ptLabel.ui_buf_length ) );

        aCertInfo.iKeyId.Copy(
            TPtr8(
                ptKeyID.pb_buf, 
                ptKeyID.ui_buf_length, 
                ptKeyID.ui_buf_length ) );
        
        aCertInfo.iCAId.Copy(
            TPtr8(
                ptCAID.pb_buf, 
                ptCAID.ui_buf_length, 
                ptCAID.ui_buf_length ) );
        
        aCertInfo.iIssuerHash.Copy(
            TPtr8(
                ptIssuerHash.pb_buf, 
                ptIssuerHash.ui_buf_length, 
                ptIssuerHash.ui_buf_length ) );

        aCertInfo.iUsage = usage;
        aCertInfo.iType = type;
        aCertInfo.iCertlen = certLen;
        aCertInfo.iModifiable = modifiable;
        
        // Certificate location
        aCertInfo.iCDFRefs = iWimUtilFuncs->MapCertLocation( uiCDFRefs );

        //Allocate enough memory for OID
        aCertInfo.iTrustedUsageLength = ptTrustedUsage.ui_buf_length * 16;

        WSL_OS_Free( ptLabel.pb_buf );
        WSL_OS_Free( ptKeyID.pb_buf );
        WSL_OS_Free( ptCAID.pb_buf );
        WSL_OS_Free( ptIssuerHash.pb_buf );
        WSL_OS_Free( ptTrustedUsage.pb_buf );
        }

    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetExtrasFromWimL
// Fetches certificate's extra info from WIM card. In this case it is
// trusted usage info.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::GetExtrasFromWimL(
    const RMessage2& aMessage, 
    CWimMemMgmt* aWimMgmt )
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::GetExtrasFromWimL | Begin"));

    WIMI_STAT callStatus = WIMI_Ok;
    TInt8 certUsage = 0;
    
    HBufC8* keyIdBuf = iWimUtilFuncs->DesLC( 0, aMessage );
    TPtrC8 keyIdHash = keyIdBuf->Des();

    WIMI_Ref_t* wimTempRef = aWimMgmt->WimRef();
    if ( !wimTempRef )
        { 
        wimTempRef = WIMI_GetWIMRef( 0 );
        aWimMgmt->SetWIMRef( wimTempRef );  // takes ownership
        }

    if ( wimTempRef )
        {
        TWimEntryType certEntryType = static_cast< TWimEntryType >( aMessage.Int2() );
        switch ( certEntryType )
            {
            case EWimEntryTypeCA:
                {
                certUsage = WIMI_CU_CA;
                break;
                }
            case EWimEntryTypeAll: //Flow through
            case EWimEntryTypePersonal:
                {
                certUsage = WIMI_CU_Client;
                break;
                }
            default:
                {
                callStatus = WIMI_ERR_BadParameters;
                break;
                }
            }

        if ( callStatus == WIMI_Ok )
            {
            callStatus = GetExtrasFromWimRefL( wimTempRef,
                                               certUsage,
                                               keyIdHash,
                                               aMessage );
            }
        }
    else
        {
        callStatus = WIMI_ERR_BadReference;
        }

    CleanupStack::PopAndDestroy( keyIdBuf );
    aMessage.Complete( CWimUtilityFuncs::MapWIMError( callStatus ) );
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetExtrasFromWimRefL
// Fetches extra information (e.g. certs trusted usage) from the WIM card.
// -----------------------------------------------------------------------------
//
WIMI_STAT CWimCertHandler::GetExtrasFromWimRefL(
    WIMI_Ref_t* aTmpWimRef,
    TInt8 aUsage,
    TDesC8& aKeyHash,
    const RMessage2& aMessage )
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::GetExtrasFromWimRefL | Begin"));
  
    TUint8 tempCertCount = 0;
    WIMI_RefList_t certRefList = NULL;
    WIMI_STAT callStatus = WIMI_Ok;
    TInt certIndex = 0;
    TPtrC8 keyHash;

    if ( aTmpWimRef )
        {
        // List all certificates (by WIM and usage)
        callStatus = WIMI_GetCertificateListByWIM( aTmpWimRef, 
                                                   aUsage, 
                                                   &tempCertCount, 
                                                   &certRefList );
        }
    else
        {
        callStatus = WIMI_ERR_BadReference;
        }
    CleanupPushWimRefListL( certRefList );
    
    if ( callStatus == WIMI_Ok )
        {
        WIMI_Ref_t* tempRef = NULL;
        WIMI_BinData_t ptLabel;
        WIMI_BinData_t ptKeyID;
        WIMI_BinData_t ptCAID;
        WIMI_BinData_t ptIssuerHash;
        WIMI_BinData_t ptTrustedUsage;
        TUint8 uiCDFRefs;
        TUint8 usage;
        TUint8 certType;
        TUint16 certLen;
        TUint8 modifiable = 0;

        for ( TInt i = 0; i < tempCertCount; i++ )
            {
            // Get info for each certificate until we find valid cert
            callStatus = WIMI_GetCertificateInfo( certRefList[i],
                                                  &tempRef,
                                                  &ptLabel,
                                                  &ptKeyID,
                                                  &ptCAID,
                                                  &ptIssuerHash,
                                                  &ptTrustedUsage,
                                                  &uiCDFRefs,
                                                  &usage,
                                                  &certType,   
                                                  &certLen,
                                                  &modifiable );
            if ( callStatus == WIMI_Ok )
                {
                free_WIMI_Ref_t( tempRef );

                // Code MAY NOT leave before ptLabel.pb_buf, ptKeyID.pb_buf,
                // ptCAID.pb_buf, ptIssuerHash.pt_buf, and ptTrustedUsage.pb_buf
                // are deallocated.

                keyHash.Set( ptKeyID.pb_buf, ptKeyID.ui_buf_length );

                // Compare given and fetched key hash
                if ( keyHash.Compare( aKeyHash ) == 0 &&
                     certType == WIMI_CT_X509 ) //Match
                    {
                    certIndex = i; // Found one
                    i = tempCertCount; // Stop looping
                    callStatus = WIMI_Ok;
                    }
                else // Cert not supported
                    {
                    callStatus = WIMI_ERR_UnsupportedCertificate;
                    }

                WSL_OS_Free( ptLabel.pb_buf );
                WSL_OS_Free( ptKeyID.pb_buf );
                WSL_OS_Free( ptCAID.pb_buf );
                WSL_OS_Free( ptIssuerHash.pb_buf );
                WSL_OS_Free( ptTrustedUsage.pb_buf );
                // Code can leave after this point.
                }
            }

        if ( callStatus == WIMI_Ok )
            {
            CopyCertExtrasInfoL( certRefList[certIndex], aMessage );
            }

        CleanupStack::PopAndDestroy( certRefList );
        }

    return callStatus;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::CopyCertExtrasInfoL
// Copies certs extra information to client's allocated memory area.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::CopyCertExtrasInfoL(
    WIMI_Ref_t* aCert,
    const RMessage2& aMessage )
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::CopyCertExtrasInfoL | Begin"));
    TUint8 pushedItemCount = 0;
    WIMI_Ref_t* tempref = NULL;
    WIMI_BinData_t ptLabel;
    WIMI_BinData_t ptKeyID;
    WIMI_BinData_t ptCAID;
    WIMI_BinData_t ptIssuerHash;
    WIMI_BinData_t ptTrustedUsage;
    TUint8 uiCDFRefs;
    TUint8 usage;
    TUint8 type;
    TUint16 certlen;
    TUint8 modifiable = 0;
    TBool found = ETrue;

    TCertExtrasInfo certExtraInfo;
    certExtraInfo.iCDFRefs = 0;
    certExtraInfo.iTrustedUsage = NULL;

    WIMI_STAT callStatus = WIMI_GetCertificateInfo( 
                                aCert,
                                &tempref,
                                &ptLabel,
                                &ptKeyID, /* Key Id (hash)*/
                                &ptCAID,
                                &ptIssuerHash,
                                &ptTrustedUsage,
                                &uiCDFRefs,
                                &usage,  /* 0 = client, 1 = CA */
                                &type,   
                                &certlen,   /* cert. content or URL length */
                                &modifiable); 
    if ( callStatus == WIMI_Ok )
        {
        free_WIMI_Ref_t( tempref );
        WSL_OS_Free( ptLabel.pb_buf );
        WSL_OS_Free( ptKeyID.pb_buf );
        WSL_OS_Free( ptCAID.pb_buf );
        WSL_OS_Free( ptIssuerHash.pb_buf );

        CleanupPushWimBufL( ptTrustedUsage );
        pushedItemCount++;

        TPtrC8 undecodedUsage;
        undecodedUsage.Set( ptTrustedUsage.pb_buf ); 

        if ( ptTrustedUsage.ui_buf_length == 0 ) // No OIDs
            {
            found = EFalse;
            }

        // DECODE OIDs
        TASN1DecObjectIdentifier decoder;
        RPointerArray<HBufC> decodedOIDArray;
        CleanupResetAndDestroyPushL( decodedOIDArray );
        pushedItemCount++;
        HBufC* decodedOIDs = NULL;
        TInt oidsLength = 0;    // length of total OID buffer
        TInt err;

        for ( TInt position = 0; found; )   //Loop until no OIDs found anymore
            {
            if ( undecodedUsage.Length() > position ) //Don't go over buffer
                {
                TRAP( err, decodedOIDs = decoder.DecodeDERL( undecodedUsage, position ) );
                if ( err == KErrNone ) //Found OID
                    {
                    CleanupStack::PushL( decodedOIDs );
                    if ( decodedOIDs->Length() )
                        {
                        found = ETrue;
                        decodedOIDArray.AppendL( decodedOIDs );
                        oidsLength += decodedOIDs->Length();
                        CleanupStack::Pop( decodedOIDs );
                        }
                    else    // Not found OID from buffer
                        {
                        found = EFalse;    
                        CleanupStack::PopAndDestroy( decodedOIDs );
                        }
                    decodedOIDs = NULL;
                    }
                else    // Error in OID parsing -> Not found OID
                    {
                    found = EFalse;
                    }
                }
            else    // undecoded OID buffer seeked through
                {
                found = EFalse;
                }
            }

        _LIT( KDelimeter16, " " ); //Delimeter between OIDs
        TInt trustedUsagesBufMaxLength = oidsLength + decodedOIDArray.Count();
        TPtr trustedUsage( NULL, 0, trustedUsagesBufMaxLength );

        if ( oidsLength > 0 ) // OID's found
            {
            // Alloc new buffer for all OID's
            HBufC* trustedUsagesBuf = HBufC::NewLC( trustedUsagesBufMaxLength );
            pushedItemCount++;

            trustedUsage.Set( trustedUsagesBuf->Des() );
       
            // Add OID's to one buffer from separate buffers
            for ( TInt i = 0; i < decodedOIDArray.Count(); i++ )
                {
                if ( i == 0 ) //First one
                    {
                    trustedUsage.Copy( decodedOIDArray[i]->Des() );
                    }
                else // Append other OIDs, with delimeter
                    {
                    trustedUsage.Append( KDelimeter16 );
                    trustedUsage.Append( decodedOIDArray[i]->Des() );
                    }
                }
            }

        TPckgBuf<TCertExtrasInfo> wimCertExtraPckg( certExtraInfo );
        aMessage.ReadL( 1, wimCertExtraPckg );
        
        wimCertExtraPckg().iCDFRefs = iWimUtilFuncs->MapCertLocation( uiCDFRefs );
        
        if ( oidsLength > 0 ) // OID's found, write buffer to client
            {
            aMessage.WriteL( 3, trustedUsage );
            }
        aMessage.WriteL( 1, wimCertExtraPckg );

        CleanupStack::PopAndDestroy( pushedItemCount, ptTrustedUsage.pb_buf );
        }
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetCerticateCountL
// Fetches count of certicates in certain WIM card.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::GetCerticateCountL(
    const RMessage2& aMessage, 
    CWimMemMgmt* aWimMgmt ) const
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::GetCerticateCountL | Begin"));
    WIMI_STAT callStatus = WIMI_Ok;
    TWimEntryType certEntryType = ( TWimEntryType )aMessage.Int1();
    
    __ASSERT_ALWAYS( certEntryType != EWimEntryTypeAll ||
        certEntryType != EWimEntryTypeCA ||
        certEntryType != EWimEntryTypePersonal, User::Leave( KErrArgument ) );

    WIMI_Ref_t* wimRef = NULL;
    TUint8 certCount = 0;

    wimRef = aWimMgmt->WimRef();
    if ( !wimRef )
        {
        wimRef = WIMI_GetWIMRef( 0 );
        aWimMgmt->SetWIMRef( wimRef );  // takes ownership
        }

    if ( wimRef )
        {
        if ( EWimEntryTypeAll == certEntryType || 
            EWimEntryTypeCA == certEntryType )
            {
            callStatus = GetCertificateCountByWIM( wimRef, 
                                                   certCount, 
                                                   WIMI_CU_CA );            
            }

        if ( callStatus == WIMI_Ok && ( EWimEntryTypeAll == certEntryType || 
                                     EWimEntryTypePersonal == certEntryType ) )
            {
            callStatus = GetCertificateCountByWIM( wimRef, 
                                                   certCount, 
                                                   WIMI_CU_Client );
            }
        }
    else
        {
        callStatus = WIMI_ERR_OutOfMemory;
        }

    if ( callStatus == WIMI_Ok )
        {
        _WIMTRACE2(_L("WIM | WIMServer | CWimCertHandler::GetCerticateCountL | count=%d"), certCount);
        TPckgBuf<TUint8> pckg( certCount );
        aMessage.WriteL( 0, pckg );
        }
    aMessage.Complete( CWimUtilityFuncs::MapWIMError( callStatus ) );
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetCertificateCountByWIM
// Fetches count of certicates in certain WIM card.
// This function MAY NOT leave.
// -----------------------------------------------------------------------------
//
WIMI_STAT CWimCertHandler::GetCertificateCountByWIM(
    WIMI_Ref_t* aRef, 
    TUint8& aCertCount, 
    TUint8 aUsage ) const
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::GetCertificateCountByWIM | Begin"));
    
    // Get the number of certificates from smart card.
    TUint8 certNum = 0;
    WIMI_RefList_t refList ;
    WIMI_STAT callStatus = WIMI_GetCertificateListByWIM( aRef, 
                                                         aUsage,
                                                         &certNum, 
                                                         &refList );

    if ( callStatus != WIMI_Ok )   
        {
    	return callStatus;
        }
    
    // Find out how many certificate has zero length
    TInt certLenZero = 0;
    TInt certMalformat = 0;
    for ( TInt i = 0; i < certNum ; i++ )
	    {
	    WIMI_Ref_t* tempRef = NULL;
	    WIMI_BinData_t ptLabel;
	    WIMI_BinData_t ptKeyID;
	    WIMI_BinData_t ptCAID;
	    WIMI_BinData_t ptIssuerHash;
	    WIMI_BinData_t ptTrustedUsage;
	    TUint8 uiCDFRefs;
	    TUint8 usage;
	    TUint8 type;
	    TUint16 certLen;
	    TUint8 modifiable = 0;
	    callStatus = WIMI_GetCertificateInfo( 
	                                refList[i],
	                                &tempRef,
	                                &ptLabel,
	                                &ptKeyID, /* Key Id (hash)*/
	                                &ptCAID,
	                                &ptIssuerHash,
	                                &ptTrustedUsage,
	                                &uiCDFRefs,
	                                &usage,  /* 0 = client, 1 = CA */
	                                &type,   /* WTLSCert(1), 
	                                            X509Cert(2), 
	                                            X968Cert(3), 
	                                            CertURL(4) */ 
	                                &certLen,   /* cert. content or URL length */
	                                &modifiable );

	    if ( callStatus == WIMI_Ok )
	        {
	        free_WIMI_Ref_t( tempRef );
	        
	        if ( certLen == 0 )
	            {
	            certLenZero++;
	            }

            // it is x509cert
            if ( type == 2 && certLen != 0 )
                {

                //use this rough sanity checking
                if ( certLen < 10 )
                    {
                    certMalformat++;
                    }
                }

            WSL_OS_Free( ptLabel.pb_buf );
            WSL_OS_Free( ptKeyID.pb_buf );
            WSL_OS_Free( ptCAID.pb_buf );
            WSL_OS_Free( ptIssuerHash.pb_buf );
            WSL_OS_Free( ptTrustedUsage.pb_buf );
	        }
	    }

    // only return the number of ceritifcates that have non-zero length
    if ( callStatus == WIMI_Ok )
        {
        aCertCount = static_cast< TUint8 >( aCertCount + certNum - certLenZero - certMalformat );
        }

    free_WIMI_RefList_t( refList );

    return callStatus;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::StoreCertificateL
// Stores certificate to the WIM card.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::StoreCertificateL( 
    TWimServRqst /*aOpcode*/,
    const RMessage2& aMessage ) const
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::StoreCertificateL | Begin"));
    aMessage.Complete( KErrNotSupported );
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::RemoveCertificateL
// Removes certificate from a WIM card.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::RemoveCertificateL( 
    const RMessage2& aMessage,
    CWimMemMgmt* /*aWimMgmt*/ ) const
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::RemoveCertificateL | Begin"));
    aMessage.Complete( KErrNotSupported );
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::SanityCheck
// -----------------------------------------------------------------------------
//
TBool CWimCertHandler::SanityCheck( TUint32 aCertRef )
    {
    TInt certNum = iCertRefLst.Count();
    if ( certNum == 0 )
        {
        return EFalse;
        }
    
    for ( TInt index = 0; index < certNum; ++index )
        {
        if( aCertRef == iCertRefLst[ index ] )
            {
            return ETrue;
            }
        }
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetCertificateDetailsL
// Fetches certificate details.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::GetCertificateDetailsL(
    TWimServRqst aOpCode, 
    const RMessage2& aMessage ) 
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimCertHandler::GetCertificateDetailsL | Begin"));
    TBool IsOk = SanityCheck( (TUint32)aMessage.Ptr0() );
    if ( ! IsOk  )
        {
        aMessage.Panic(_L("WIM"), KErrBadHandle );
        return;
        }
    
    //capability checking
    TUint8 usage = 255;
    WIMI_STAT callStatus = ResolveCertUsage( aMessage, usage );
    if ( WIMI_Ok == callStatus )
        {
        if( !CheckReadCapsForUsage( aMessage, usage  ) )
              {
              aMessage.Complete(KErrPermissionDenied);  
              return;
              }
        }

    //capabilty ok, go to fetch the details of the certificate.
    WIMI_Ref_pt pCertRef = const_cast<WIMI_Ref_pt>( aMessage.Ptr0() );
    CWimResponse* responseID = new( ELeave ) CWimResponse( aMessage );
    CleanupStack::PushL( responseID );
    TWimReqTrId* trId = iWimUtilFuncs->TrIdLC( responseID, EWimMgmtReq );
    responseID->iOpcode = aOpCode;
    CleanupStack::Pop( 2, responseID ); // trId, responseID

    WIMI_STAT certReqStatus = WIMI_CertificateReq( trId, pCertRef );
    if ( certReqStatus != WIMI_Ok )
        {
        responseID->iStatus = certReqStatus;
        responseID->CompleteMsgAndDelete();
        delete trId;
        }
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::ResolveCertUsage
// Resolves usage (CA/User) for a certificate.
// -----------------------------------------------------------------------------
//
WIMI_STAT CWimCertHandler::ResolveCertUsage( const RMessage2& aMsg,
                                        TUint8& aUsage )
    {
    _WIMTRACE(_L("CWimServer::ResolveCertUsage | Begin"));

    // aMsg.Ptr0 contains reference to certificate
    
    WIMI_Ref_pt pCertRef = const_cast< WIMI_Ref_pt >( aMsg.Ptr0() );
    _WIMTRACE2(_L("CWimServer::ResolveCertUsage | Begin aMsg.Ptr0() = %d"), aMsg.Ptr0());

    WIMI_STAT callStatus = GetCertificateInfo( pCertRef, aUsage );

    _WIMTRACE(_L("CWimServer::ResolveCertUsage | End"));
    return callStatus;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetCertificateInfo
// Fetches certificate info. Wrapper for WIMI call.
// -----------------------------------------------------------------------------
//
WIMI_STAT CWimCertHandler::GetCertificateInfo( WIMI_Ref_pt aCertRef,
                                          TUint8& aUsage )
    {
    _WIMTRACE(_L("CWimServer::GetCertificateInfo | Begin"));

    WIMI_Ref_pt     pWimRef = NULL;
    WIMI_BinData_t  ptLabel;
    WIMI_BinData_t  ptKeyID;
    WIMI_BinData_t  ptCAID;
    WIMI_BinData_t  ptIssuerHash;
    WIMI_BinData_t  ptTrustedUsage;
    TUint8          uiCDFRefs;
    TUint8          type;
    TUint16         certLen;
    TUint8          modifiable = 0;
    _WIMTRACE(_L("CWimServer::GetCertificateInfo | Begin 1"));
    WIMI_STAT callStatus = WIMI_GetCertificateInfo(
                                aCertRef,
                                &pWimRef,
                                &ptLabel,
                                &ptKeyID, // Key Id (hash)
                                &ptCAID,
                                &ptIssuerHash,
                                &ptTrustedUsage,
                                &uiCDFRefs,
                                &aUsage,  // 0 = client, 1 = CA
                                &type,    // WTLSCert(1),
                                          // X509Cert(2),
                                          // X968Cert(3),
                                          // CertURL(4)
                                &certLen, // cert. content or URL length
                                &modifiable );
    _WIMTRACE(_L("CWimServer::GetCertificateInfo | Begin 2"));
    // Don't need references anymore
    if ( callStatus == WIMI_Ok )
        {
        free_WIMI_Ref_t( pWimRef );
        WSL_OS_Free( ptLabel.pb_buf );
        WSL_OS_Free( ptKeyID.pb_buf );
        WSL_OS_Free( ptCAID.pb_buf );
        WSL_OS_Free( ptIssuerHash.pb_buf );
        WSL_OS_Free( ptTrustedUsage.pb_buf );
        }

    _WIMTRACE(_L("CWimServer::GetCertificateInfo | End"));
    return callStatus;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::GetCertificateInfo
// Fetches certificate info. Wrapper for WIMI call.
// -----------------------------------------------------------------------------
//
TBool CWimCertHandler::CheckReadCapsForUsage( const RMessage2& aMsg,
                               TUint8 aUsage )
    {
    TBool result = EFalse;
    
    switch ( aUsage )
        {
        case WIMI_CU_CA:
            {
            // CA certificate reading doesn't require any capability.
            _WIMTRACE(_L("CWimCertHandler::CheckReadCapsForUsage: CA cert read req, OK."));
            result = ETrue;
            break;
            }
        case WIMI_CU_Client:
            {
            // User certificate reading requires ReadUserData capability.
            if ( aMsg.HasCapability( ECapabilityReadUserData ))
                {
                result = ETrue;
                _WIMTRACE(_L("CWimCertHandler::CheckReadCapsForUsage: User cert read capa PASS"));
                }
            else
                {
                result = EFalse;
                _WIMTRACE(_L("CWimCertHandler::CheckReadCapsForUsage: User cert read capa FAIL"));
                }
            break;
            }
        default:
            {
            _WIMTRACE(_L("CWimCertHandler::CheckReadCapsAccordingToUsage: FAIL:Unknown usage."));
            result = EFalse;
            break;
            }
        }
    _WIMTRACE(_L("CWimCertHandler::CheckReadCapsForUsage | End"));
    return result;
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::ExportPublicKeyL
// Export public key from certificate.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::ExportPublicKeyL( const RMessage2& aMessage ) const
    {
    _WIMTRACE(_L("WIM | WIMServer | CWimPublicKeyHandler::ExportPublicKeyParamsL | Begin"));

    TUint8 certType = 0;

    TPckgBuf<TExportPublicKey> keyExportPckg;
    aMessage.ReadL( 0, keyExportPckg );
    
    HBufC8* publicKeyBuf = HBufC8::NewLC( KPublicKeyLength );

    TBuf8<KKeyIdLen> keyIdBuf = keyExportPckg().iKeyId;

    WIMI_STAT callStatus = WIMI_Ok;
    WIMI_BinData_t ptCertData;
    ptCertData.pb_buf = NULL;
    ptCertData.ui_buf_length = 0;
    WIMI_Ref_t* pCert = NULL;
    WIMI_TransactId_t trId = ( void* )EInitializationCertListHashing;
    TUint8 certCount = 0;
    WIMI_RefList_t certRefList = NULL;

    // Check firs the Client certs
    callStatus = WIMI_GetCertificateListByKeyHash( ( TUint8* )keyIdBuf.Ptr(),
                                                   WIMI_CU_Client,
                                                   &certCount,
                                                   &certRefList );
    if ( callStatus == WIMI_Ok )
        {
        if ( certCount == 0 ) // Not found client certs
            {
            free_WIMI_RefList_t( certRefList );
            certRefList = NULL;

            // Check CA certs
            callStatus = WIMI_GetCertificateListByKeyHash(
                                                    ( TUint8* )keyIdBuf.Ptr(),
                                                    WIMI_CU_CA,
                                                    &certCount,
                                                    &certRefList );
            }
        }

    if ( callStatus == WIMI_Ok )
        {
        CleanupPushWimRefListL( certRefList );
        if ( certCount == 0 ) // No certificates found for key
            {
            CleanupStack::PopAndDestroy( certRefList );
            callStatus = WIMI_ERR_CertNotFound;
            }
        }

    if ( callStatus == WIMI_Ok )
        {
        WIMI_Ref_t* tempRef = NULL;
        WIMI_BinData_t ptLabel;
        WIMI_BinData_t ptKeyID;
        WIMI_BinData_t ptCAID;
        WIMI_BinData_t ptIssuerHash;
        WIMI_BinData_t ptTrustedUsage;
        TUint8 uiCDFRefs;
        TUint8 usage;

        TUint16 certLen;
        TUint8 modifiable = 0;

        for ( TInt i = 0; i < certCount; i++ )
            {
            // Get info for each certificate until we find valid cert
            callStatus = WIMI_GetCertificateInfo( certRefList[i],
                                                  &tempRef,
                                                  &ptLabel,
                                                  &ptKeyID,
                                                  &ptCAID,
                                                  &ptIssuerHash,
                                                  &ptTrustedUsage,
                                                  &uiCDFRefs,
                                                  &usage,
                                                  &certType,   
                                                  &certLen,
                                                  &modifiable );
            if ( callStatus == WIMI_Ok )
                {
                WSL_OS_Free( ptLabel.pb_buf );
                WSL_OS_Free( ptKeyID.pb_buf );
                WSL_OS_Free( ptCAID.pb_buf );
                WSL_OS_Free( ptIssuerHash.pb_buf );
                WSL_OS_Free( ptTrustedUsage.pb_buf );
                free_WIMI_Ref_t( tempRef );

                // Certificate has to be WTLS or X509, not URL
                if ( certType == WIMI_CT_X509 || certType == WIMI_CT_WTLS )
                    {
                    pCert = certRefList[i]; // Found one
                    i = certCount; // Stop looping
                    }
                else // Cert not supported
                    {
                    callStatus = WIMI_ERR_UnsupportedCertificate;
                    }
                }
            }
    
        // Retrieve certificate data from WIM
        if ( callStatus == WIMI_Ok )
            {
            callStatus = WIMI_CertificateReqT( trId, pCert, &ptCertData );
            if ( callStatus == WIMI_Ok )
                {
                CleanupPushWimBufL( ptCertData );

                TPtrC8 certPtr( ptCertData.pb_buf, ptCertData.ui_buf_length );
                TPtr8 publicKeyPtr = publicKeyBuf->Des();

                TRAPD( parseOk, ParseCertPublicKeyL( certPtr, publicKeyPtr, certType ) );
                if ( parseOk )
                    {
                    aMessage.WriteL( 1, publicKeyPtr );
                    }
                else
                    {
                    callStatus = WIMI_ERR_CertParseError;
                    }

                CleanupStack::PopAndDestroy( ptCertData.pb_buf );
                }
            }
        
        CleanupStack::PopAndDestroy( certRefList );
        }

    CleanupStack::PopAndDestroy( publicKeyBuf );

    aMessage.Complete( CWimUtilityFuncs::MapWIMError( callStatus ) );
    }

// -----------------------------------------------------------------------------
// CWimCertHandler::ParseCertPublicKeyL
// Parse public key from certificate.
// -----------------------------------------------------------------------------
//
void CWimCertHandler::ParseCertPublicKeyL(
    const TDesC8& aCertData,
    TDes8& aPublicKey,
    const TUint8 aCertType ) const
    {
    CCertificate* certificate = NULL;
    CRSAPublicKey* publicKey = NULL;

    switch ( aCertType )
        {
        case WIMI_CT_WTLS:
            {
            certificate = CWTLSCertificate::NewLC( aCertData );
            publicKey = CWTLSRSAPublicKey::NewLC( certificate->PublicKey().KeyData() );
            break;
            }
    
        case WIMI_CT_X509:
            {
            certificate = CX509Certificate::NewLC( aCertData );
            publicKey = CX509RSAPublicKey::NewLC( certificate->PublicKey().KeyData() );
            break;
            }
        default:
            {
            _WIMTRACE2(_L("WIM|WIMServer|CWimCertHandler::ParseCertPublicKeyL, type %d not supported"), aCertType);
            User::Leave( KErrNotSupported );
            break;
            }
        }

    TX509RSAKeyEncoder encoder( *publicKey, ESHA1 );
    CASN1EncBase* encoded = encoder.EncodeKeyLC();

    TUint pos = 0;
    aPublicKey.SetLength( KPublicKeyLength );
    // Check that Max. length is not exceeded
    if ( encoded->LengthDER() > static_cast< TUint >( KPublicKeyLength ) )
        {
        _WIMTRACE(_L("WIM|WIMServer|CWimCertHandler::ParseCertPublicKeyL, too long public key"));
        User::Leave( KErrBadDescriptor );
        }
    // Write encoded key to prealloced buffer
    encoded->WriteDERL( aPublicKey, pos );

    CleanupStack::PopAndDestroy( 3, certificate );  // encoded, publicKey, certificate
    }

//  End of File
