Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
New Developer
inSanyac
Posts: 9
Registered: ‎02-27-2014
My Device: None
Accepted Solution

Proper Triple DES Encryption

Hi everybody,

I’m stuck with this problem for hours now. Maybe somebody here can help my with that.
I’m trying to encrypt a string using the Triple DES algorithm (using hu_des.h). The following configuration is important:
- CBC Mode
- PKCS7 Padding
- Random IV

As far as the documentation says, I need the following setup:

- A global context
- A random context (to create an IV)
- DES Params
- DES Key

So I started with the global context and its initialization:

 

int returnCode;

returnCode = hu_GlobalCtxCreateDefault(&sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_GlobalCtxCreateDefault");
}

returnCode = hu_RegisterSbg56DES(sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_RegisterSbg56DES");
}

returnCode = hu_RegisterSbg56(sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_RegisterSbg56");
}

returnCode = hu_InitSbg56(sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_InitSbg56");
}

returnCode = hu_RegisterSystemSeed(sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_RegisterSystemSeed");
}

returnCode = hu_RngDrbgCreate(HU_DRBG_CIPHER, 192, false, 0, NULL, NULL, &rngCtx, sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_RngDrbgCreate");
}

 So far so good. There are no problems here. Next is the random IV generation:

 

int returnCode;

unsigned char *iv = new unsigned char[SB_DES_IV_SIZE];

returnCode = hu_RngGetBytes(rngCtx, SB_DES_IV_SIZE, iv, sbCtx);
if (returnCode != SB_SUCCESS)
{
        cleanup(returnCode, "hu_RngGetBytes");
} char *ivData = reinterpret_cast <char*>(iv);
ByteArray data = QByteArray::fromRawData(ivData, SB_DES_IV_SIZE);
return data;

I don’t know if there are already some problems here. What would be the correct initialization of the random context?
Since I got no error here, I assumed everything was alright.

I think I did nothing wrong defining the DES params:

 

int returnCode;

sb_Params desedeParams = sb_Params();
returnCode = hu_DESParamsCreate(SB_DES_TDES, SB_DES_CBC, SB_DES_PARITY_OFF, SB_DES_WEAK_KEY_OFF, rngCtx, NULL, &desedeParams, sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_DESParamsCreate");
}

return desedeParams;

 Also creating the DES key seemed easy:

 

int returnCode;

sb_Key desedeKey = sb_Key();
returnCode = hu_DESKeySet(params, SB_DES_KEY_SIZE, cryptoKey(), SB_DES_KEY_SIZE, cryptoKey(), SB_DES_KEY_SIZE, cryptoKey(), &desedeKey, sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_DESKeySet");
}
return desedeKey;

I know that Triple DES usually uses 3 different Keys to encrypt, decrypt and encrypt again and to improve key length but in this case I only have one single key. My client uses the exact same key for the iOS Version of the app, so it needs to be the same.
Here is how I generate the key in „cryptoKey()“ (of course I changed all the values):

 

static uint8_t *cryptoKey = NULL;

if(!cryptoKey)
{
	cryptoKey = (uint8_t *)malloc(SB_DES_KEY_SIZE);

	unsigned char bytes[] = {
		   A, LOT, OF, DIFFERENT, DATA, 0x89, 0x2C, 0xAF,
	        0xCA, 0x31, 0x4B, 0x15, 0x7C, 0xB5, 0x80, 0xC3,
	        0xF6, 0x50, 0x42, 0xA4, 0x97, 0xFF, 0x21, 0x08
	};

	memcpy(cryptoKey, bytes, SB_DES_KEY_SIZE);
}

	return cryptoKey;

 

Okay now, here goes the encryption:

 

int returnCode;

// Params
sb_Params params = getTDESParams();

// Key creation
sb_Key key = getTDESKey(params);

// IV Generation
int ivSize = iv.size();
unsigned char *ivBytes = reinterpret_cast<unsigned char*>(iv.data());

// Padding (8)
QByteArray paddedBytes = pad(data, SB_DES_BLOCK_SIZE);
qDebug() << "PADDED SIZE: " << paddedBytes.size();

// Input
int inputSize = paddedBytes.size();
unsigned char *inputData = reinterpret_cast<unsigned char*>(paddedBytes.data());

// Output
unsigned char desCipherText[paddedBytes.size()];

returnCode = hu_DESEncryptMsg(params, key, ivSize, ivBytes, inputSize, inputData, desCipherText, sbCtx);
if (returnCode != SB_SUCCESS)
{
	cleanup(returnCode, "hu_DESEncryptMsg");
}

QByteArray byteData = QByteArray::fromRawData(reinterpret_cast<const char *>(desCipherText), sizeof(desCipherText));

return byteData;

 

That’s where I’m not sure if I do everything right. What’s the proper way to ensure the padding is correct? Also I get an error regarding the IV length but it’s defined as SB_DES_IV_LENGTH in the IV generation method.
Usually the input data has to be a multiple of SB_DES_BLOCK_SIZE, is that correct? I know, here it’s not the case but I’ve done tons of tests but no success.

It would be great if somebody could help me with that or rather tell me wether I made some essential mistakes.

 

Greetings,

insane aka Phil

Retired
robbieDubya
Posts: 418
Registered: ‎07-18-2012
My Device: Q10

Re: Proper Triple DES Encryption

Hi,

 

Was your code based on the AESCryptoDemo sample? https://github.com/blackberry/Cascades-Community-Samples/tree/master/AESCryptoDemo

 

Please post more code - if it compiles/runs it's going to be easier to see if something is up.

 

Thanks.

--
Rob is no longer associated with BlackBerry.
New Developer
inSanyac
Posts: 9
Registered: ‎02-27-2014
My Device: None

Re: Proper Triple DES Encryption

[ Edited ]

Hi,

thanks for your reply.

 

No, the Code is based on a TDESDemo I found here: http://www.2shared.com/file/ObFx_7d_/TDesDemo.html

I modified the params and some other things in order to match my configuration.

 

Also here is my complete Code of my Crypto Class:

 

Crypto::Crypto()
{
	int returnCode;

	returnCode = hu_GlobalCtxCreateDefault(&sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_GlobalCtxCreateDefault");
	}

	returnCode = hu_RegisterSbg56DES(sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_RegisterSbg56DES");
	}

	returnCode = hu_RegisterSbg56(sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_RegisterSbg56");
	}

	returnCode = hu_InitSbg56(sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_InitSbg56");
	}

	returnCode = hu_RegisterSystemSeed(sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_RegisterSystemSeed");
	}

	returnCode = hu_RngDrbgCreate(HU_DRBG_CIPHER, 192, false, 0, NULL, NULL, &rngCtx, sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_RngDrbgCreate");
	}
}

Crypto::~Crypto()
{
}

QByteArray Crypto::generateIV()
{
    int returnCode;

    unsigned char *iv = new unsigned char[SB_DES_IV_SIZE];
    //memset((void *) iv, 0x0, (size_t) sizeof(iv));

    returnCode = hu_RngGetBytes(rngCtx, SB_DES_IV_SIZE, iv, sbCtx);
    if (returnCode != SB_SUCCESS)
    {
    	cleanup(returnCode, "hu_RngGetBytes");
    }

    char *ivData = reinterpret_cast <char*>(iv);

    QByteArray data = QByteArray::fromRawData(ivData, SB_DES_IV_SIZE);

    return data;
}

sb_Params Crypto::getTDESParams()
{
	int returnCode;

	sb_Params desedeParams = sb_Params();
	returnCode = hu_DESParamsCreate(SB_DES_TDES, SB_DES_CBC, SB_DES_PARITY_OFF, SB_DES_WEAK_KEY_OFF, rngCtx, NULL, &desedeParams, sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_DESParamsCreate");
	}

	return desedeParams;
}

sb_Key Crypto::getTDESKey(sb_Params params)
{
	int returnCode;

	sb_Key desedeKey = sb_Key();
	returnCode = hu_DESKeySet(params, SB_DES_KEY_SIZE, cryptoKey(), SB_DES_KEY_SIZE, cryptoKey(), SB_DES_KEY_SIZE, cryptoKey(), &desedeKey, sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_DESKeySet");
	}

	return desedeKey;
}

QByteArray Crypto::encrypt(QByteArray data, QByteArray iv)
{
	int returnCode;

	// Params
	sb_Params params = getTDESParams();

	// Key creation
	sb_Key key = getTDESKey(params);

	// IV Generation
	int ivSize = iv.size();
	unsigned char *ivBytes = reinterpret_cast<unsigned char*>(iv.data());

	// Padding (8)
	QByteArray paddedBytes = pad(data, SB_DES_BLOCK_SIZE);
	qDebug() << "PADDED SIZE: " << paddedBytes.size();

	// Input
	int inputSize = paddedBytes.size();
	unsigned char *inputData = reinterpret_cast<unsigned char*>(paddedBytes.data());

	// Output
	unsigned char desCipherText[paddedBytes.size()];

	returnCode = hu_DESEncryptMsg(params, key, ivSize, ivBytes, inputSize, inputData, desCipherText, sbCtx);
	if (returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_DESEncryptMsg");
	}

	QByteArray byteData = QByteArray::fromRawData(reinterpret_cast<const char *>(desCipherText), sizeof(desCipherText));

	return byteData;
}


QByteArray Crypto::decrypt(QByteArray data, QByteArray iv)
{
	int returnCode;

	// Params
	sb_Params desedeParams = getTDESParams();

	// Key creation
	sb_Key desedeKey = getTDESKey(desedeParams);

	// IV Generation
	int ivSize = iv.size();
	unsigned char *ivBytes = reinterpret_cast<unsigned char*>(iv.data());

	// Padding (8)
	QByteArray paddedBytes = pad(data, 8);
	qDebug() << "PADDED SIZE: " << paddedBytes.size();

	// Input
	int inputSize = paddedBytes.size();
	unsigned char *inputData = reinterpret_cast<unsigned char*>(paddedBytes.data());

	// Output
	unsigned char plaintext[paddedBytes.size()];

	returnCode = hu_DESDecryptMsg(desedeParams, desedeKey, ivSize, ivBytes, inputSize, inputData, plaintext, sbCtx);
	if(returnCode != SB_SUCCESS)
	{
		cleanup(returnCode, "hu_DESDecryptMsg");
	}

	QByteArray plainBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(plaintext), inputSize);

	return plainBytes;
}

QByteArray Crypto::pad(QByteArray in, int blockSize)
{
	int padLength = blockSize - (in.length() % blockSize);

	QByteArray out = QByteArray();

	for (int i = 0; i < padLength; ++i)
	{
		out.append((char) padLength);
	}

	return out;
}

void Crypto::cleanup(int returnCode, QString method)
{
	QString hexString = QString::number(returnCode, 16);

	cout << "Errorcode: " << hexString.toStdString() << " in: " << method.toStdString() << endl;
}

uint8_t* Crypto::cryptoKey()
{
	static uint8_t *cryptoKey = NULL;

	if(!cryptoKey)
	{
		cryptoKey = (uint8_t *)malloc(SB_DES_KEY_SIZE);

		unsigned char bytes[] = {
			0x38, 0xC1, 0xF9, 0x94, 0xD4, 0x59, 0x7F, 0x88,
		        0xAC, 0x11, 0x76, 0x35, 0x3A, 0xC3, 0xC0, 0xD3,
		        0xA8, 0x40, 0x33, 0xB4, 0x17, 0xCF, 0x71, 0x79
		};

		memcpy(cryptoKey, bytes, SB_DES_KEY_SIZE);
	}

	return cryptoKey;
}

 And that's how I call these methods:

 

//IV & Crypto
Crypto *myCrypto = new Crypto();
QByteArray iv = myCrypto->generateIV();

//Data
QByteArray data("12345678");

//Encryption
QString encPinConst =  QString(myCrypto->encrypt(data, iv));
qDebug() << "Encrypted: "  << encPinConst;

//Decryption
QByteArray decPinConst = QByteArray(encPinConst.toUtf8());
QByteArray decPinConst1 = myCrypto->decrypt(decPinConst, iv);
qDebug() << "Decrypted: " << decPinConst1;

 

 I really appreciate your help, I hope you can help me.

 

Edit: I will try the AES Demo you mentioned and post my results

Retired
robbieDubya
Posts: 418
Registered: ‎07-18-2012
My Device: Q10

Re: Proper Triple DES Encryption

[ Edited ]

Hi,

 

It mostly works; here's what I can see:

 

  • UTF8 Conversion
    • Cipher text is binary data. UTF8 is not. UTF8 has rules about what makes a valid character and encoding. If you convert cipher text through UTF8 and back again - you're going to have a bad time.
  • QByteArray::FromRaw is used badly, in two different ways.
    • Memory Leak. Some code in here will do new char *, and pass if off to the QByteArray. QByteArray never frees the buffer given to it via a FromRaw.
    • Stack Buffer. Some code in here uses a char array that is on the stack. When you return that QByteArray you're going to see whatever data is currently on the stack, not the content that was in your buffer N frames ago.
  • Ignoring all Errors
    • You are logging the errors - but you keep on going. A low level C interface like this will seg fault when it tells you to stop but you keep giving it garbage.
  • Padding
    • In the encrypt step - you're only attempting to encrypt the padding. You're not attempting to encrypt (plaintext+padding).
    • In the decrypt step - you're trying to pad before decrypting. ie: decrypt(encrypt(plain+pad)+pad). The correct way is to decrypt and check that it finished with valid padding. ie: depad(decrypt(encrypt(plain+pad)).

 

Thanks.

 

--
Rob is no longer associated with BlackBerry.
New Developer
inSanyac
Posts: 9
Registered: ‎02-27-2014
My Device: None

Re: Proper Triple DES Encryption

Thanks for your help.

After following your steps I could figure out most of my errors. It still isn't perfect but with a little more work I should be able to get it working!

Retired
robbieDubya
Posts: 418
Registered: ‎07-18-2012
My Device: Q10

Re: Proper Triple DES Encryption

Hi,

 

Just noticed that you're also using the same buffer for each key param in hu_desKeySet.

 

The code has a 24 byte single key, but keySet expects three 8 byte keys (in TDES mode).

 

Thanks.

 

 

--
Rob is no longer associated with BlackBerry.
New Developer
inSanyac
Posts: 9
Registered: ‎02-27-2014
My Device: None

Re: Proper Triple DES Encryption

Hi,

 

you are right. I was using the same part of the key multiple times.

Now it works perfectly!

 

Thank you again!

New Developer
robbieDubyaII
Posts: 3
Registered: ‎04-04-2014
My Device: Nexus 5

Re: Proper Triple DES Encryption

You're welcome! Glad to see that it's working!

New Developer
yogibong
Posts: 16
Registered: ‎10-17-2011
My Device: 9700

Re: Proper Triple DES Encryption

Hi InSanyac,

 

experienced same issue here, i want to use triple des with single key.

With the suggestion from robbieDubbya, how i'm gonna fix your code?

 

And if my key is "SmartPhone", in what function that I must state this key?

 

 

Kindly Advise

Thank you.

New Developer
inSanyac
Posts: 9
Registered: ‎02-27-2014
My Device: None

Re: Proper Triple DES Encryption

[ Edited ]

Hi yogibong,

 

you should not use my code since I used an example-code from the web and it was not satisfying my needs.

What really made my encryption work is this example from BlackBerry:

AESCryptoDemo

 

You can edit this example and use TDES encyption instead of AES. Keep in mind what robbie said: The code has a 24 byte single key, but keySet expects three 8 byte keys (in TDES mode).

 

Your code should then look like this:

 

uint8_t *key = NULL;

//your raw key
unsigned char bytes[] = {
	0x10, 0xC1, 0xF9, 0x9A, 0xB4, 0x48, 0x1F, 0xEF,
	0xBA, 0xA1, 0x6B, 0xA5, 0x3B, 0xC7, 0x30, 0xD3,
	0xAC, 0x60, 0x22, 0xA4, 0xB7, 0xAC, 0x11, 0x05
};

//prepare key length
size_t keyLength = 3 * SB_DES_KEY_SIZE;

//prepare key space
key = (uint8_t *)malloc(keyLength);
memcpy(key, bytes, keyLength);

//generate TDES key
int rc = hu_DESKeySet(_params.tdesParams(),
	8, key,
	8, &key[8],
	8, &key[16],
	&_tdesKey, _params.globalContext().ctx());

 

Let me know if you need further information.

 

Cheerio,

insane