BlackBerry 10, 3DES-ECB implementation

94 Views Asked by At

Intro

My bb10 app sent 3DES-ECB-encrypted data to backend (PHP)

Code

PHP backend

public static function encrypt3Des($data, $key)
{
    //Generate a key from a hash
    $key = md5(utf8_encode($key), true);
    //Take first 8 bytes of $key and append them to the end of $key.
    $key .= substr($key, 0, 8);
    //Pad for PKCS7
    $blockSize = mcrypt_get_block_size('tripledes', 'ecb');
    $len = strlen($data);
    $pad = $blockSize - ($len % $blockSize);
    $data = $data.str_repeat(chr($pad), $pad);
    //Encrypt data
    $encData = mcrypt_encrypt('tripledes', $key, $data, 'ecb');
    return base64_encode($encData);
}

public static function decrypt3Des($data, $secret)
{
    //Generate a key from a hash
    $key = md5(utf8_encode($secret), true);
    //Take first 8 bytes of $key and append them to the end of $key.
    $key .= substr($key, 0, 8);
    $data = base64_decode($data);
    $data = mcrypt_decrypt('tripledes', $key, $data, 'ecb');
    $block = mcrypt_get_block_size('tripledes', 'ecb');
    $len = strlen($data);
    $pad = ord($data[$len-1]);
    return substr($data, 0, strlen($data) - $pad);
}

Blackberry 10 client

/// cipherservice.hpp ///

#pragma once

#include <QObject>

#include <hudes.h>
#include <hugse56.h>

class CipherService : public QObject {
    Q_OBJECT
public:

    virtual ~CipherService();
    CipherService(QString key);

    QString encryptTDES(QString message);
    QString decryptTDES(QString message);

private:
    void initCipher(QString key);
    void destroyCipher();

    sb_GlobalCtx huGlobalContext;
    sb_Context huContext;
    sb_Key huKey;
    sb_Params huParams;
};

/// cipherservice.cpp ///

#include "cipherservice.hpp"

#include <huctx.h>

#define TO_HEXSTR(code) ("0x" + QString("%1").arg(code, 0, 16).toUpper())

CipherService::CipherService(QString key)
    : QObject() {
    initCipher(key);
}

CipherService::~CipherService() {
    destroyCipher();
}

void CipherService::initCipher(QString key) {
    int rc = hu_GlobalCtxCreateDefault(&huGlobalContext);
    LOGD << "hu_GlobalCtxCreateDefault return code = " << TO_HEXSTR(rc);

    rc = hu_RegisterSbg56DES(huGlobalContext);
    LOGD << "hu_RegisterSbg56DES return code = " << TO_HEXSTR(rc);

    rc = hu_InitSbg56(huGlobalContext);
    LOGD << "hu_InitSbg56 return code = " << TO_HEXSTR(rc) << " (0xF00D means already initialized so ignore it)";

    rc = hu_DESParamsCreate(USED_SB_DES, SB_DES_ECB, SB_DES_PARITY_OFF, SB_DES_WEAK_KEY_OFF,
            NULL, NULL, &huParams, huGlobalContext);
    LOGD << "hu_DESParamsCreate return code = " << TO_HEXSTR(rc);

    QByteArray resultKey = QCryptographicHash::hash(key.toUtf8().constData(), QCryptographicHash::Md5);
    resultKey.append(resultKey.mid(0, 8));

    LOGD << "key=" << resultKey.toBase64();

    unsigned char* keyBuf = reinterpret_cast<unsigned char*>(resultKey.data());
    size_t keyBufLen = SB_DES_KEY_SIZE;
    rc = hu_DESKeySet(huParams, keyBufLen, keyBuf, keyBufLen, keyBuf, keyBufLen, keyBuf, &huKey, huGlobalContext);
    LOGD << "hu_DESKeySet return code = " << TO_HEXSTR(rc);

    const unsigned char iv[SB_DES_IV_SIZE] = {0};
    rc = hu_DESBeginV2(huParams, huKey, SB_DES_ECB, SB_DES_IV_SIZE, iv, &huContext, huGlobalContext);
    LOGD << "hu_DESBeginV2 return code = " << TO_HEXSTR(rc);
}

void CipherService::destroyCipher() {
    if (&huContext != NULL) {
        int rc = hu_DESEnd(&huContext, huGlobalContext);
        LOGD << "hu_DESEnd return code = " << TO_HEXSTR(rc);
        huContext = NULL;
    }
}

QString CipherService::encryptTDES(QString msg) {
    size_t pad = SB_DES_BLOCK_SIZE - (msg.length() % SB_DES_BLOCK_SIZE);

    for (uint i = 0; i < pad; i += 1) {
        msg.append(static_cast<QChar>(pad));
    }

    QByteArray plainBuf(msg.toUtf8());
    char* cipherBuf = new char[plainBuf.length()];

    LOGD << "hu_DESEncryptMsg plainBuf = " << plainBuf;

    int rc = hu_DESEncrypt(huContext,
                plainBuf.length(),
                reinterpret_cast<const unsigned char*>(plainBuf.data()),
                reinterpret_cast<unsigned char*>(cipherBuf), huGlobalContext);

    QByteArray byteData((char*)cipherBuf, plainBuf.length());
    QString result(byteData.toBase64());

    LOGD << "hu_DESEncryptMsg length = " << plainBuf.length() << " code = " << TO_HEXSTR(rc) << " result = " << result;

    return result;
}

QString CipherService::decryptTDES(QString msg) {
    QByteArray cipherBuf(QByteArray::fromBase64(msg.toUtf8()));

    if (cipherBuf.size() % SB_DES_BLOCK_SIZE) {
        cipherBuf.resize((cipherBuf.size() / SB_DES_BLOCK_SIZE) * (SB_DES_BLOCK_SIZE));
    }
    LOGD << "hu_DESEncryptMsg cipherBuf = " << msg << " length = " << cipherBuf.length();

    unsigned char* plainBuf = new unsigned char[cipherBuf.length() + 1](); // +1 to last \0

    int rc = hu_DESDecrypt(huContext,
            cipherBuf.length(),
            reinterpret_cast<const unsigned char*>(cipherBuf.data()),
            reinterpret_cast<unsigned char*>(plainBuf), huGlobalContext);

    QByteArray byteData((char*)plainBuf, cipherBuf.length());
    QString result(byteData);

    if (result.length() > 0) {
        size_t pad = (size_t) result[result.length() - 1].toAscii();
        result = result.remove(result.length() - pad, pad);
    }

    LOGD << "hu_DESDecryptMsg length = " << cipherBuf.length() << " code = " << TO_HEXSTR(rc) << " result = " << result;

    return result;
}

Problem

This solution doesn't work, during troubleshooting I have found 2 main differences between PHP and BB10 implementations:

Questions

  • Is it possible to use different key lengths on BlackBerry10 Cascades?
  • What is the crypt library used there? Is it from QNX?
  • I saw that there is openssl presented on BB10, but it's API looks very limited, is it possible to implement 3DES-ECB with openssl on BB10?

Links

1

There are 1 best solutions below

8
zaph On

The issue is the key. BB creates the 24-byte 3DES key like this:

QByteArray resultKey = QCryptographicHash::hash(key.toUtf8().constData(), QCryptographicHash::Md5);
resultKey.append(resultKey.mid(0, 8));

It is taking the key which is a string , funs it through MD% creating 16-bytes. Then it appends the first 8-bytes of the MD5 output to it. This creates a 24-byte key, what is known as two-key 3DES, this is not secure and should be used.

For PHP you need to create the key in the same way.

Also pay attention to the IV and padding. As noted ECB mode does not use an IV and I have seen time and again pan IV being supplied the ECB mode, =generally because developers did not understand ECB mode.

You are probably better off using OpenSSL in PHP, mcrypt is horrible.

Use some hex prints to verify that the key and IV are the same prior to encryption.