diff -r 000000000000 -r e35f40988205 xmlsecurityengine/xmlseccrypto/src/xmlsecc_cryptowrapper.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/xmlsecurityengine/xmlseccrypto/src/xmlsecc_cryptowrapper.cpp Thu Dec 17 09:29:21 2009 +0200 @@ -0,0 +1,831 @@ +/* +* Copyright (c) 2005-2006 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: Methods that allows to encrypt and decrypt data. +* +*/ + +#include +#include +#include +#include + +// reading different cipher algorithm +#include <3des.h> +#include +#include +#include +#include +#include +#include +#include +#include + +/** A port of gnu useful functions to Symbian **/ +#include "xmlsecc_config.h" +#include "xmlsecc_globals.h" + +#include "xmlsec_error_flag.h" +#include "xmlsecc_padding.h" + +#include "xmlsecc_cryptowrapper.h" + +// set block size according to "Symmetric ciphers - How To" +//#define MAX_BLOCKSIZE 16 +const TInt KBlockSize( 8 ); +const TInt KAESBlockSize( 16 ); + +#define CTX_MAGIC_NORMAL 0x24091964 +#define CTX_MAGIC_SECURE 0x46919042 + +const TInt KDESKeySize( 8 ); +const TInt K3DESKeySize( 24 ); +const TInt KAESKeySize128( 16 ); +const TInt KAESKeySize192( 24 ); +const TInt KAESKeySize256( 32 ); + + +/* The handle structure. */ +struct sc_cipher_handle +{ + int magic; + size_t actual_handle_size; /* Allocated size of this handle. */ + CSymmetricCipher* iEncryptor; // CBlockTransformation + CSymmetricCipher* iDecryptor; + HBufC8* iKey; + TInt algo; + size_t blocksize; + int mode; + unsigned int flags; + HBufC8* iv; + int unused; /* in IV */ + +}; + + +// Call Symbian random generator +void doRandomizeL(unsigned char* buffer, size_t length) + { + HBufC8* hbuf = HBufC8::NewLC(length); + TPtr8 ptr = hbuf->Des(); + ptr.FillZ(ptr.MaxLength()); + + CSystemRandom* rand=CSystemRandom::NewLC(); + rand->GenerateBytesL(ptr); + memcpy(buffer, hbuf->Ptr(), length); + CleanupStack::PopAndDestroy(rand); + CleanupStack::PopAndDestroy(hbuf); + } + + +/** + - Function: void sc_randomize (unsigned char *BUFFER, size_t LENGTH, + enum sc_random_level LEVEL) + **/ +TInt sc_randomize(unsigned char* buffer, size_t length, enum sc_random_level level) + { + TInt leaveValue( KErrNone ); + TRAP(leaveValue, doRandomizeL (buffer, length)); + + if ( leaveValue != KErrNone ) + { + xmlSecSetErrorFlag( leaveValue ); + } + return leaveValue; + } + +/* Retrieve the block length used with algorithm A. */ +size_t sc_cipher_get_algo_blklen (int algo) +{ + switch (algo) { + // set block size according to "Symmetric ciphers - How To" + case SC_CIPHER_AES128: + case SC_CIPHER_AES192: + case SC_CIPHER_AES256: + return KAESBlockSize; + case SC_CIPHER_NONE: + case SC_CIPHER_3DES: + case SC_CIPHER_DES: + default: + return KBlockSize; // 8 bytes + + } + +} + +/* Set the IV to be used for the encryption context C to IV with + length IVLEN. The length should match the required length. */ +TInt +sc_cipher_setiv( sc_cipher_hd_t c, const byte *iv, unsigned int ivlen ) + { + TInt error( KErrNone ); + + if( iv ) { + + if (c->iv) + { + delete c->iv; + c->iv = NULL; + } + + + if( ivlen != c->blocksize ) + { + /* + log_info("WARNING: cipher_setiv: ivlen=%u blklen=%u\n", + ivlen, (unsigned) c->blocksize ); + */ + } + if (ivlen > c->blocksize) + ivlen = c->blocksize; + + TInt len = ivlen; + TRAP( error, c->iv = HBufC8::NewL(ivlen) ); + if ( error ) //!= KErrNone + { + xmlSecSetErrorFlag( error ); + } + else + { + TPtrC8 ptr(iv, ivlen); + *(c->iv) = ptr; + } + + } + c->unused = 0; + + return error; + } + +/* Set the proper Symbian data structures */ +void +cipher_encrypt_prerequisiteL (sc_cipher_hd_t c) +{ + + if (c->iEncryptor) + return; + + // Generate key and encryptor/decryptor + CBlockTransformation* bT_encrypt = NULL; + CBlockTransformation* encryptor = NULL; + + switch(c->algo) + { + + case SC_CIPHER_AES128: + case SC_CIPHER_AES192: + case SC_CIPHER_AES256: + bT_encrypt = CAESEncryptor::NewLC(c->iKey->Des()); + CleanupStack::Pop(bT_encrypt); + break; + case SC_CIPHER_3DES: + bT_encrypt = C3DESEncryptor::NewLC(c->iKey->Des()); + CleanupStack::Pop(bT_encrypt); + break; + case SC_CIPHER_DES: + bT_encrypt = CDESEncryptor::NewLC(c->iKey->Des()); + CleanupStack::Pop(bT_encrypt); + break; + case SC_CIPHER_NONE: + c->iEncryptor = CNullCipher::NewL(); + break; + default: + User::Leave(KErrNotSupported); + break; + } + + switch (c->mode) + { + case SC_CIPHER_MODE_ECB: + break; + case SC_CIPHER_MODE_CBC: + + CleanupStack::PushL(bT_encrypt); + encryptor = CModeCBCEncryptor::NewLC(bT_encrypt, c->iv->Des()); + CleanupStack::Pop(encryptor); + CleanupStack::Pop(bT_encrypt); + + break; + default: + break; + } + + CleanupStack::PushL(encryptor); + + if (!c->iEncryptor) + { + if (!encryptor) + { + User::Leave(KErrGeneral); + } + CPadding* ePadding = CXmlSecPadding::NewLC(encryptor->BlockSize()); + c->iEncryptor = CBufferedEncryptor::NewL(encryptor, ePadding); + CleanupStack::Pop(ePadding); + + } + + CleanupStack::Pop(encryptor); +} + + +static TInt +cipher_encrypt_doProcessL (sc_cipher_hd_t c, byte *outbuf, const byte *inbuf, + unsigned int nbytes, int last) + { + TInt rc(KErrNone); + + switch( c->mode ) { + + case SC_CIPHER_MODE_ECB: + case SC_CIPHER_MODE_CBC: + { + TUint blockSize = c->iEncryptor->BlockSize(); + + if ((c->mode == SC_CIPHER_MODE_CBC) + && (nbytes%blockSize && !last) + && !(nbytes > blockSize + && (c->flags & SC_CIPHER_CBC_CTS))) + { + rc = KErrArgument; + } + else if ((c->mode == SC_CIPHER_MODE_ECB) + && (nbytes%blockSize && !last)) + { + rc = KErrArgument; + } + else + { + TInt outlen; + if (last) + outlen = c->iEncryptor->MaxFinalOutputLength(nbytes); + else + outlen = c->iEncryptor->MaxOutputLength(nbytes); + HBufC8 *result = HBufC8::NewLC(outlen); + TPtr8 resultActual= result->Des(); + resultActual.FillZ(resultActual.MaxLength()); + resultActual.SetLength(0); + + // could improve memory usage later by allocating a smaller buffer + HBufC8 *input = HBufC8::NewLC(nbytes); + TPtrC8 inbufPtr(inbuf, nbytes); + *input = inbufPtr; + + TInt j(0); + for(; j+blockSizeiEncryptor->Process(input->Mid(j,blockSize), resultActual); + } + + if (last) + { + c->iEncryptor->ProcessFinalL(input->Mid(j), resultActual); + // we delete iEncryptor once the decryption finishes + // so that the next decryption can use a different key or iv + delete c->iEncryptor; + c->iEncryptor = NULL; + } + else + c->iEncryptor->Process(input->Mid(j,blockSize), resultActual); + + memcpy(outbuf, result->Ptr(), result->Size()); + CleanupStack::PopAndDestroy(input); + CleanupStack::PopAndDestroy(result); + } + } + break; + /* + case SC_CIPHER_MODE_CFB: + do_cfb_encrypt(c, outbuf, inbuf, nbytes ); + break; + case SC_CIPHER_MODE_CTR: + do_ctr_encrypt(c, outbuf, inbuf, nbytes ); + break; + case SC_CIPHER_MODE_STREAM: + c->cipher->stencrypt ( &c->context.c, + outbuf, (byte*)inbuf, nbytes ); + break; + case SC_CIPHER_MODE_NONE: + if( inbuf != outbuf ) + memmove( outbuf, inbuf, nbytes ); + break; + */ + default: + rc = KErrNotSupported; + break; + + } + + return rc; + } + +/**************** + * Encrypt INBUF to OUTBUF with the mode selected at open. + * inbuf and outbuf may overlap or be the same. + * Depending on the mode some contraints apply to NBYTES. + */ +static TInt +cipher_encrypt (sc_cipher_hd_t c, byte *outbuf, + const byte *inbuf, unsigned int nbytes, int last) +{ + TInt leaveValue; + TInt ret(KErrNone); + + // Perform Symbian prerequisite data structure initialization + TRAP(leaveValue, cipher_encrypt_prerequisiteL (c)); + if (leaveValue) //!= KErrNone; + { + xmlSecSetErrorFlag( leaveValue ); + return leaveValue; + } + + // Symbian encryption + TRAP(leaveValue, ret=cipher_encrypt_doProcessL (c, outbuf, inbuf, nbytes, last)); + if (leaveValue) //!= KErrNone + { + xmlSecSetErrorFlag( leaveValue ); + return leaveValue; + } + return ret; +} + +/**************** + * Encrypt IN and write it to OUT. If IN is NULL, in-place encryption has + * been requested. + */ +int +sc_cipher_encrypt (sc_cipher_hd_t h, void *out, size_t outsize, + const void *in, size_t inlen, int last) +{ + int err; + + if (!in) + /* Caller requested in-place encryption. */ + /* Actullay cipher_encrypt() does not need to know about it, but + * we may change this to get better performance. */ + + err = cipher_encrypt (h, (byte *)out, (byte *)out, outsize, last); + else if (outsize < ((h->flags & SC_CIPHER_CBC_MAC) ? + h->blocksize : inlen)) + err = KErrArgument; + else if ((h->mode == SC_CIPHER_MODE_ECB + || (h->mode == SC_CIPHER_MODE_CBC + && (! ((h->flags & SC_CIPHER_CBC_CTS) + && (inlen > h->blocksize))))) + && ((inlen % h->blocksize) && !last)) // let Symbian do the padding + err = KErrArgument; + else + err = cipher_encrypt (h, (byte *)out, (byte *)in, inlen, last); + + if (err && out) + memset (out, 0x42, outsize); /* Failsafe: Make sure that the + plaintext will never make it into + OUT. */ + + return err; +} + +/* Set the proper Symbian data structures */ +void +cipher_decrypt_prerequisiteL (sc_cipher_hd_t c) +{ + + if (c->iDecryptor) + return; + + // Generate key and encryptor/decryptor + CBlockTransformation* bT_decrypt = NULL; + CBlockTransformation* decryptor = NULL; + + switch(c->algo) + { + + case SC_CIPHER_AES128: + case SC_CIPHER_AES192: + case SC_CIPHER_AES256: + bT_decrypt = CAESDecryptor::NewLC(c->iKey->Des()); + CleanupStack::Pop(bT_decrypt); + break; + case SC_CIPHER_3DES: + bT_decrypt = C3DESDecryptor::NewLC(c->iKey->Des()); + CleanupStack::Pop(bT_decrypt); + break; + case SC_CIPHER_DES: + bT_decrypt = CDESDecryptor::NewLC(c->iKey->Des()); + CleanupStack::Pop(bT_decrypt); + break; + case SC_CIPHER_NONE: + c->iDecryptor = CNullCipher::NewL(); + break; + default: + User::Leave(KErrNotSupported); + break; + } + + switch (c->mode) + { + case SC_CIPHER_MODE_ECB: + break; + case SC_CIPHER_MODE_CBC: + CleanupStack::PushL(bT_decrypt); + decryptor = CModeCBCDecryptor::NewLC(bT_decrypt, c->iv->Des()); + CleanupStack::Pop(decryptor); + CleanupStack::Pop(bT_decrypt); + + break; + default: + break; + } + + CleanupStack::PushL(decryptor); + + if (!c->iDecryptor) + { + if (!decryptor) + { + User::Leave(KErrGeneral); + } + CPadding* dPadding = CXmlSecPadding::NewLC(decryptor->BlockSize()); + c->iDecryptor = CBufferedDecryptor::NewL(decryptor, dPadding); + CleanupStack::Pop(dPadding); + + } + + CleanupStack::Pop(decryptor); +} + +static TInt +cipher_decrypt_doProcessL (sc_cipher_hd_t c, byte *outbuf, const byte *inbuf, + unsigned int nbytes, int* outputLen, int last) + { + TInt rc(KErrNone); + + switch( c->mode ) { + case SC_CIPHER_MODE_ECB: + case SC_CIPHER_MODE_CBC: + { + TUint blockSize = c->iDecryptor->BlockSize(); + + if ((c->mode == SC_CIPHER_MODE_CBC) + && nbytes%blockSize + && !(nbytes > blockSize + && (c->flags & SC_CIPHER_CBC_CTS))) + { + rc = KErrArgument; + } + else if ((c->mode == SC_CIPHER_MODE_ECB) + && nbytes%blockSize) + { + rc = KErrArgument; + } + else + { + TInt outlen; + if (last) + outlen = c->iDecryptor->MaxFinalOutputLength(nbytes); + else + outlen = c->iDecryptor->MaxOutputLength(nbytes); + HBufC8 *result = HBufC8::NewLC(outlen); + TPtr8 resultActual= result->Des(); + resultActual.FillZ(resultActual.MaxLength()); + resultActual.SetLength(0); + + // could improve memory usage later by allocating a smaller buffer + HBufC8* input = HBufC8::NewLC(nbytes); + TPtrC8 inbufPtr(inbuf, nbytes); + *input = inbufPtr; + + TInt j=0; + + for(; j+blockSizeiDecryptor->Process(input->Mid(j,blockSize), resultActual); + } + + if (last) + { + c->iDecryptor->ProcessFinalL(input->Mid(j), resultActual); + *outputLen = result->Size(); + + // we delete iDecryptor once the decryption finishes + // so that the next decryption can use a different key or iv + delete c->iDecryptor; + c->iDecryptor = NULL; + } + else + c->iDecryptor->Process(input->Mid(j,blockSize), resultActual); + + memcpy(outbuf, result->Ptr(), result->Size()); + + CleanupStack::PopAndDestroy(input); + CleanupStack::PopAndDestroy(result); + + } + } + break; + /* + case SC_CIPHER_MODE_CFB: + do_cfb_decrypt(c, outbuf, inbuf, nbytes ); + break; + case SC_CIPHER_MODE_CTR: + do_ctr_decrypt(c, outbuf, inbuf, nbytes ); + break; + case SC_CIPHER_MODE_STREAM: + c->cipher->stdecrypt ( &c->context.c, + outbuf, (byte*) inbuf, nbytes ); + break; + case SC_CIPHER_MODE_NONE: + if( inbuf != outbuf ) + memmove( outbuf, inbuf, nbytes ); + break; + */ + default: + rc = KErrNotSupported; + break; + + } + + return rc; + } + +/**************** + * Decrypt INBUF to OUTBUF with the mode selected at open. + * inbuf and outbuf may overlap or be the same. + * Depending on the mode some some contraints apply to NBYTES. + */ +static TInt +cipher_decrypt (sc_cipher_hd_t c, byte *outbuf, const byte *inbuf, + unsigned int nbytes, int *outlen, int last) +{ + TInt rc( KErrNone ); + TInt leaveValue; + + // Perform Symbian prerequisite data structure initialization + TRAP(leaveValue, cipher_decrypt_prerequisiteL (c)); + if (leaveValue != KErrNone) + { + xmlSecSetErrorFlag( leaveValue ); + return leaveValue; + } + + // Symbian decryption + TRAP(leaveValue, rc=cipher_decrypt_doProcessL (c, outbuf, inbuf, nbytes, outlen, last)); + if (leaveValue != KErrNone) + { + xmlSecSetErrorFlag( leaveValue ); + return leaveValue; + } + return rc; +} + + + +int +sc_cipher_decrypt (sc_cipher_hd_t h, void *out, size_t outsize, + const void *in, size_t inlen, int* outlen, int last) +{ + TInt err( KErrNone ); + + if (last && !outlen) + err = KErrArgument; + else if (! in) + /* Caller requested in-place encryption. */ + /* Actullay cipher_encrypt() does not need to know about it, but + * we may chnage this to get better performance. */ + err = cipher_decrypt (h, (byte*)out, (byte*)out, outsize, outlen, last); + else if (inlen == 0) + return err; + else if (outsize < inlen) + err = KErrArgument; + else if (((h->mode == SC_CIPHER_MODE_ECB) + || ((h->mode == SC_CIPHER_MODE_CBC) + && (! ((h->flags & SC_CIPHER_CBC_CTS) + && (inlen > h->blocksize))))) + && (inlen % h->blocksize)) + err = KErrArgument; + else + err = cipher_decrypt (h, (byte*)out, (byte*)in, inlen, outlen, last); + + return err; +} + +/* + Open a cipher handle for use with cipher algorithm ALGORITHM, using + the cipher mode MODE (one of the SC_CIPHER_MODE_*) and return a + handle in HANDLE. Put NULL into HANDLE and return an error code if + something goes wrong. FLAGS may be used to modify the + operation. The defined flags are: + + SC_CIPHER_SECURE: allocate all internal buffers in secure memory. + SC_CIPHER_ENABLE_SYNC: Enable the sync operation as used in OpenPGP. + SC_CIPHER_CBC_CTS: Enable CTS mode. + SC_CIPHER_CBC_MAC: Enable MAC mode. + + Values for these flags may be combined using OR. + */ +TInt +sc_cipher_open (sc_cipher_hd_t *handle, + int algo, int mode, unsigned int flags) +{ + int secure = (flags & SC_CIPHER_SECURE); + CSymmetricCipher* encryptor = NULL; //CBlockTransformation + CSymmetricCipher* decryptor = NULL; + sc_cipher_hd_t h = NULL; + TInt err( KErrNone ); + + /* If the application missed to call the random poll function, we do + it here to ensure that it is used once in a while. */ + + /* check flags */ + if ((! err) + && ((flags & ~(0 + | SC_CIPHER_SECURE + | SC_CIPHER_ENABLE_SYNC + | SC_CIPHER_CBC_CTS + | SC_CIPHER_CBC_MAC)) + || (flags & SC_CIPHER_CBC_CTS & SC_CIPHER_CBC_MAC))) + err = KErrArgument; + + /* check that a valid mode has been requested */ + if (! err) + switch (mode) + {// + case SC_CIPHER_MODE_ECB: + case SC_CIPHER_MODE_CBC: + case SC_CIPHER_MODE_CFB: + case SC_CIPHER_MODE_CTR: + case SC_CIPHER_MODE_STREAM: + break; + case SC_CIPHER_MODE_NONE: + break; + + default: + err = KErrNotSupported; + } + + + if (! err) + { + size_t size = sizeof(*h); + h = (sc_cipher_hd_t)malloc(size); + + if ( !h ) + { + err = KErrNoMemory; + xmlSecSetErrorFlag( KErrNoMemory ); + } + else + { + h->magic = secure ? CTX_MAGIC_SECURE : CTX_MAGIC_NORMAL; + h->actual_handle_size = size; + h->iEncryptor = encryptor; + h->iDecryptor = decryptor; + h->iKey = NULL; + h->algo = algo; + h->iv = NULL; + + h->mode = mode; + h->flags = flags; + + if (algo == SC_CIPHER_NONE) + { + TRAPD(leaveValue,h->iEncryptor = CNullCipher::NewL()); + if (leaveValue) //!= KErrNone + { + xmlSecSetErrorFlag( leaveValue ); + return leaveValue; + } + TRAP(leaveValue,h->iDecryptor = CNullCipher::NewL()); + if (leaveValue) //!= KErrNone + { + xmlSecSetErrorFlag( leaveValue ); + return leaveValue; + } + + } + } + } + + *handle = err ? NULL : h; + + return err; +} + + +/* Release all resources associated with the cipher handle H. H may be + NULL in which case this is a no-operation. */ +void +sc_cipher_close (sc_cipher_hd_t h) +{ + if (! h) + return; + + if ((h->magic != CTX_MAGIC_SECURE) + && (h->magic != CTX_MAGIC_NORMAL)) + { + /* + _sc_fatal_error(GPG_ERR_INTERNAL, + "sc_cipher_close: already closed/invalid handle"); + */ + return; + } + + else + h->magic = 0; + + if (h->iEncryptor) + { + delete h->iEncryptor; + h->iEncryptor = NULL; + } + + if (h->iDecryptor) + { + delete h->iDecryptor; + h->iDecryptor = NULL; + } + + if (h->iv) + { + delete h->iv; + h->iv = NULL; + } + + if (h->iKey) + { + delete h->iKey; + h->iKey = NULL; + } + + + /* We always want to wipe out the memory even when the context has + been allocated in secure memory. The user might have disabled + do the wiping. To accomplish this we need to keep track of the + actual size of this structure because we have no way to known + how large the allocated area was when using a standard malloc. */ + /* + TInt len = h->actual_handle_size; + memset(h, 'a', len); // fill with something + */ + + free (h); +} + +size_t +sc_cipher_get_algo_keylen (int algo) +{ + switch (algo) { + + case SC_CIPHER_AES128: + return KAESKeySize128; + case SC_CIPHER_AES192: + return KAESKeySize192; + case SC_CIPHER_AES256: + return KAESKeySize256; + case SC_CIPHER_3DES: + return K3DESKeySize; + case SC_CIPHER_DES: + return KDESKeySize; + case SC_CIPHER_NONE: + default: + return 0; + + } + +} + + +/* Set the key to be used for the encryption context C to KEY with + length KEYLEN. The length should match the required length. */ +//sc_error_t +TInt sc_cipher_setkey (sc_cipher_hd_t c, byte *key, unsigned int keylen) + { + + // Change key into the right format + TRAPD(error, c->iKey = HBufC8::NewL(keylen)); + if(error == KErrNone) + { + TPtrC8 keyPtr(key, keylen); + *(c->iKey) = keyPtr; + } + else + { + xmlSecSetErrorFlag( error ); + } + return error; + } + +/* Set specification blocksize in context */ +void set_ctx_blocksize(sc_cipher_hd_t c, size_t bklen) +{ + c->blocksize = bklen; +}