How to create a CryptoSPI plug-in

Introduction

Note : this document is intended for device manufacturers.

The purpose of a CryptoSPI plug-in is to implement one or more cryptographic algorithms.

Symbian provides a plug-in called softwarecrypto.dll that implements all algorithms supported by the legacy (pre-Symbian^3) cryptography APIs. In Symbian^3, the legacy APIs have all been re-implemented to use this plug-in DLL. The source code for softwarecrypto.dll is located under src/common/generic/security/cryptospi/source/softwarecrypto/ .

This is a summary of the steps involved in creating a plug-in, in no particular order. Details of each step are given in the rest of the document.

How to create the MMP file

See for instance src/common/generic/security/cryptospi/group/softwarecrypto.mmp .

       
        
       
       TARGET softwarecrypto.dll
TARGETTYPE dll

UID        0x1000008d 0x102835C2
VENDORID     0x70000001

CAPABILITY    All

DEFFILE        softwarecrypto.def

USERINCLUDE        .
USERINCLUDE        ..\inc 
USERINCLUDE        ..\inc\spi
SYSTEMINCLUDE    \epoc32\include
SYSTEMINCLUDE    \epoc32\include\cryptospi

SOURCEPATH    ..\source\softwarecrypto
SOURCE pluginentry.cpp
SOURCE md2impl.cpp md5impl.cpp sha1impl.cpp hmacimpl.cpp
SOURCE 3desimpl.cpp desimpl.cpp rc2impl.cpp rijndaelimpl.cpp arc4impl.cpp symmetriccipherimpl.cpp
SOURCE randomimpl.cpp dsasignerimpl.cpp dsaverifyimpl.cpp rsaimpl.cpp rsafunction.cpp
SOURCE signerimpl.cpp verifierimpl.cpp asymmetriccipherimpl.cpp

LIBRARY euser.lib cryptospi.lib

// Depends on bigint and padding code
LIBRARY cryptography.lib
//Depends on random server for random number generation
LIBRARY random.lib
      

Key points:

  • 0x1000008d identifies the DLL as static (not polymorphic or an ECOM plug-in) and 0x102835C2 is unique to this plug-in, allocated by Symbian Signed.

  • It is recommended that plug-ins have ALL capabilities because this ensures that they can be loaded by client applications with any capabilities.

  • The project needs to link against cryptospi.lib , the CryptoSPI library.

  • The plug-in needs to link against cryptography.lib because big integers ( RInteger ) are implemented in cryptography.dll .

  • SYSTEMINCLUDE \epoc32\include\cryptospi allows code to #include the CryptoSPI header files, which are all exported to epoc32\include\cryptospi\ .

How to define the algorithm's characteristics

All plug-ins must define the characteristics of their algorithm implementations that are fixed at compile time, as constant data. Some characteristics are common to all interface types, for instance the name and UID of the algorithm implemented, the name of the plug-in vendor, whether the plug-in uses hardware acceleration, and whether it is FIPS certified. Common characteristics are defined in CryptoSpi::TCommonCharacteristics :

       
        
       
       class TCommonCharacteristics
    {
    public:
      ...
      TInt32 iInterfaceUID;
      TInt32 iAlgorithmUID;
      TInt32 iImplementationUID;
      const TRomLitC16* iCreatorName;
      TBool iIsFIPSApproved;
      TBool iIsHardwareSupported;
      TUint iMaxConcurrencySupported;
      const TRomLitC16* iAlgorithmName;
      TInt iLatency;
      TInt iThroughput;
      };
      

Other characteristics are specific to a particular interface. For each interface type, a T class is defined as an aggregation of a TCommonCharacteristics object and some additional characteristics particular to that interface. For instance, THashCharacteristics includes a block size, output size and operation mode. (The operation mode can be either KHashMode or KHmacMode , and indicates whether the hash algorithm is an HMAC or not.)

       
        
       
       class THashCharacteristics
    {
    public:
      IMPORT_C TBool IsOperationModeSupported(TUid aOperationMode) const;        
        
    public:
      TCommonCharacteristics cmn;
      TUint iBlockSize;
      TUint iOutputSize;
      const TInt32* iSupportedOperationModes;
      TUint iOperationModeNum;
      };
      

All the data types relating to characteristics are defined in plugincharacteristics.h .

For an example, see src/common/generic/security/cryptospi/source/softwarecrypto/pluginconfig.h .

How to implement the framework code

Plug-in modules must implement a defined set of functions exported at defined ordinals. The function prototypes and the ordinals (see TPluginEntryOrdinal ) are defined in pluginentrydef.h . When a client requests an algorithm, these functions are used by CryptoSPI to query and instantiate algorithm implementations. The simplest way to ensure that functions are assigned the correct ordinal is to copy the softwarecryptoU.def exports file, which can be found in the EABI and BWINS directories in the CryptoSPI source code.

Minimally, the plug-in DLL must implement the function exported at ordinal 1, whose prototype is:

       
        
       
       typedef const TCharacteristics** (*EnumerateCharacteristicsFunc)(TUid, TInt&);
      

and at least one of the algorithm factory functions. See for example class CCryptoPluginEntry ( pluginentry.h / pluginentry.cpp ).

If the plug-in DLL does not support a particular algorithm, the ABSENT keyword should be used in its .def file to ensure any subsequent exports are at the correct ordinals.

Querying the characteristics of plug-ins

CryptoSPI builds up a list of the characteristics of all the plug-in DLLs by calling the function exported at ordinal 1 ( EEnumerateCharacteristicsOrdinal ) for each DLL and for each interface type. Here is an example implementation:

       
        
       
       EXPORT_C const TCharacteristics** CCryptoPluginEntry::Enumerate(TUid aInterface, TInt& aNumPlugins)
    {
    const TCharacteristics** ptr(0);
    switch (aInterface.iUid)
        {
    case KHashInterface:
        {
        aNumPlugins=sizeof(KHashCharacteristics)/sizeof(THashCharacteristics*);
        ptr = (const TCharacteristics**) &KHashCharacteristics[0];
        }
        break;

    case KRandomInterface:
        {
        aNumPlugins=sizeof(KRandomCharacteristics)/sizeof(TRandomCharacteristics*);
        ptr= (const TCharacteristics**) &KRandomCharacteristics[0];
        }
        break;
  ...
  }
 return ptr;
 }
      

Key points:

  • Every plug-in must implement this function.

  • TUid aInterface is the interface UID, as defined in CryptoSpi::KInterfacesUids .

  • TInt& aNumPlugins should be set to the number of algorithms of that interface type implemented by the plug-in.

Extended characteristics

Extended characteristics are those which can only be determined at runtime. They are retrieved on demand by CryptoSPI or clients can call the plug-in's implementation of CryptoSpi::CCryptoBase::GetExtendedCharacteristicsL() .

The function exported at ordinal 2 has this prototype:

       
        
       
       typedef void (*GetExtendedCharacteristicsFuncL)(TUid, CExtendedCharacteristics*&);
      

Symbian defines 2 extended characteristics:

  • TInt iAvailableConcurrency;

    The number of available resources for processing the operation for the requested plug-in. This could be zero even if no operations are in progress; for instance cryptographic acceleration hardware is turned off to save battery power.

  • TBool iExclusiveUse;

    Whether it is possible for the application to reserve exclusive use of hardware resources used by the plug-in.

Additional plug-in specific characteristics may be defined. The plug-in creator needs to define UIDs for each of these and specify the UID when calling CryptoSpi::CExtendedCharacteristics::AddCharacteristicL() .

Algorithm instantiation

A plug-in must implement one or more factory methods for instantiating algorithms. The functions exported at ordinals 3 to 20 have the following declarations. (Note that symmetric key generation is not yet implemented by Symbian's software crypto plug-in, so the ordinals ECreateSymmetricKeyGeneratorOrdinal and ECreateAsyncSymmetricKeyGeneratorOrdinal have no corresponding declarations).

       
        
       
       typedef void (*CreateRandomFuncL)(MRandom*&, TUid, const CCryptoParams*);
typedef void (*CreateHashFuncL)(MHash*&, TUid, TUid, const CKey*, const CCryptoParams*);
typedef void (*CreateSymmetricCipherFuncL)(MSymmetricCipher*&, TUid, const CKey&, TUid, TUid, TUid, const CCryptoParams*);
typedef void (*CreateAsymmetricCipherFuncL)(MAsymmetricCipher*&, TUid, const CKey&, TUid, TUid, const CCryptoParams*);
typedef void (*CreateSignerFuncL)(MSigner*&, TUid, const CKey&, TUid, const CCryptoParams*);
typedef void (*CreateVerifierFuncL)(MVerifier*&, TUid, const CKey&, TUid, const CCryptoParams*);
typedef void (*CreateKeyAgreementFuncL)(MKeyAgreement*&, TUid, const CKey&, const CCryptoParams*);
typedef void (*CreateKeyPairGeneratorFuncL)(MKeyPairGenerator*&, TUid, const CCryptoParams*);
typedef void (*CreateAsyncRandomFuncL)(MAsyncRandom*&, TUid, const CCryptoParams*);
typedef void (*CreateAsyncHashFuncL)(MAsyncHash*&, TUid, TUid, const CKey*, const CCryptoParams*);
typedef void (*CreateAsyncSymmetricCipherFuncL)(MAsyncSymmetricCipher*&, TUid, const CKey&, TUid, TUid, TUid, const CCryptoParams*);
typedef void (*CreateAsyncAsymmetricCipherFuncL)(MAsyncAsymmetricCipher*&, TUid, const CKey&, TUid, TUid, const CCryptoParams*);
typedef void (*CreateAsyncSignerFuncL)(MAsyncSigner*&, TUid, const CKey&, TUid, const CCryptoParams*);
typedef void (*CreateAsyncVerifierFuncL)(MAsyncVerifier*&, TUid, const CKey&, TUid, const CCryptoParams*);
typedef void (*CreateAsyncKeyAgreementFuncL)(MAsyncKeyAgreement*&, TUid, const CKey&, const CCryptoParams*);
typedef void (*CreateAsyncKeyPairGeneratorFuncL)(MAsyncKeyPairGenerator*&, TUid, const CCryptoParams*);
      

Clients instantiate algorithms by calling one of the CryptoSPI factory methods, for instance CHashFactory::CreateHashL() . All CryptoSPI factory methods take a CCryptoParams parameter. This class allows clients to supply arbitrary, algorithm-specific data to plug-ins, for instance the effective key length for RC2, or the number of bytes to discard from the keystream for ARC4.

The data type of each parameter can be integer ( TInt ), big integer ( RInteger ), or 8/16 bit descriptor. Additional data types could be added in future, by extending the TParamType enum in CCryptoParam and deriving a class from CCryptoParam , but this is unlikely to be necessary.

How to implement the MPlugin-derived class

All concrete algorithm classes are derived from one of the abstract base classes listed below, which are in turn all derived from MPlugin .

  • Random ( MRandom / MAsyncRandom )

  • Hash ( MHash / MAsyncHash )

  • Symmetric cipher ( MSymmetricCipher / MAsyncSymmetricCipher )

  • Asymmetric cipher ( MAsymmetricCipher / MAsyncAsymmetricCipher )

  • Signer ( MSigner / MAsyncSigner )

  • Verifier ( MVerifier / MAsyncVerifier )

  • Key agreement ( MKeyAgreement / MAsyncKeyAgreement )

  • Key pair generator ( MKeyPairGenerator / MAsyncKeyPairGenerator )

  • Key generator ( MSymmetricKeyGenerator / MAsyncSymmetricKeyGenerator )

MPlugin ( cryptoplugin.h ) declares the following pure virtual functions:

       
        
       
       virtual void Close() = 0;
virtual void Reset() = 0;
virtual void GetCharacteristicsL(const TCharacteristics*& aPluginCharacteristics) = 0;
virtual const CExtendedCharacteristics& GetExtendedCharacteristicsL() = 0;
virtual TAny* GetExtension(TUid aExtensionId) = 0;
      

For example, the random number generator plug-in implemented in softwarecrypto.dll implements these functions as follows:

       
        
       
       void CRandomImpl::Close()
    {
    delete this;
    }

void CRandomImpl::Reset()
    { //Not required
    }

const CExtendedCharacteristics* CRandomImpl::GetExtendedCharacteristicsL()
 // All Symbian software plug-ins have unlimited concurrency and cannot be reserved
    // for exclusive use 
    {
    return CExtendedCharacteristics::NewL(KMaxTInt, EFalse);
    }

//Get the plug-in characteristics (defined at compile time)
void CRandomImpl::GetCharacteristicsL(const TCharacteristics*& aPluginCharacteristics)
    {
 //Find out how many algorithms are implemented by the plug-in
    TInt randomNum = sizeof(KRandomCharacteristics)/sizeof(TRandomCharacteristics*);
    for (TInt i = 0; i < randomNum; i++)
        {
  //Compare implementation UIDs
  //ImplementationUid() is a helper function that returns KCryptoPluginRandomUid
        if (KRandomCharacteristics[i]->cmn.iImplementationUID == ImplementationUid().iUid)
            {
            aPluginCharacteristics = KRandomCharacteristics[i];
            break;
            }
        }
    }

TAny* CRandomImpl::GetExtension(TUid /*aExtensionId*/)
    {
    return NULL;
    }
      

The main purpose of MPlugin::Reset() is to reset the hardware, so is usually not relevant to a software-only implementation. GetExtension() is intended for internal use, and may be removed from the API in future.

These functions are called through the client API to CryptoSPI, in other words CCryptoBase and the various classes derived from it, in this case, CRandom . For instance when the client calls CCryptoBase::GetCharacteristicsL() , this calls GetCharacteristicsL() in the plug-in, or calling CRandom::GenerateRandomBytesL() , calls GenerateRandomBytesL() in the plug-in.

The configuration file

The set of available plug-ins is listed in the configuration file z:\resource\cryptospi\plug-ins.txt . Each line in the file contains the filename of a single plug-in DLL, without the path. The path is not required because the DLLs are assumed to be located in z:\sys\bin . The configuration file must be on the Z: drive and therefore additional plug-ins cannot be installed post-manufacture. Depending on the plug-in selector in use, plug-ins may be loaded individually, as required, or all at once when CryptoSPI is initialized. The default selector implemented by Symbian ( CLegacySelector ) loads plug-in DLLs as required.