0

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

CAMOBAP
  • 5,523
  • 8
  • 58
  • 93
  • It is best not to use PHP mcrypt, it is abandonware, has not been updated in years and does not support standard PKCS#7 (née PKCS#5) padding, only non-standard null padding that can't even be used with binary data. mcrypt has many outstanding [bugs](https://sourceforge.net/p/mcrypt/bugs/) dating back to 2003. The mcrypt-extension is deprecated will be removed in PHP 7.2. – zaph Nov 19 '17 at 20:23

1 Answers1

0

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.

zaph
  • 111,848
  • 21
  • 189
  • 228
  • thanks for answer, Generally: 1. important point that I cannot change PHP backend because I didn't control it (I just used used sandbox to compare implementations step by step). 2. Good point about security but here is the same, I cannot change it. – CAMOBAP Nov 20 '17 at 07:09
  • Technically: 1. I cannot pass `NULL` as `IV` on BB10 side because I will get error on `hu_DESBeginV2` call. 2. I have tried to pass `24` byte key to `hu_DESKeySet`, and also error code was returned (it accept only 8-byke keys) 3. I have compared keys and data before encrypt they are both the same on PHP and BB10 – CAMOBAP Nov 20 '17 at 07:09
  • 1. When using ECB m ode do not pass an IV. 2. Add them to the question in hex as well as the sample input and output. Simply put, if you supply the same key and data to two 3DES implementations using ECB mode and using the same padding method the encrypted result will be the same. – zaph Nov 20 '17 at 13:14
  • if I will not pass IV, i will get error code [`SB_ERR_NULL_IV`](https://developer.blackberry.com/native/reference/core/com.qnx.doc.crypto.lib_ref/topic/manual/sbcrypto_return_codes.html), according to [bb10 doc](https://developer.blackberry.com/native/reference/core/com.qnx.doc.crypto.lib_ref/topic/hu_DESBeginV2.html) IV for ECB is ignored. BTW what do you mean by `same padding method` ? – CAMOBAP Nov 21 '17 at 19:15
  • There are different [padding methods](https://en.wikipedia.org/wiki/Padding_(cryptography)). Padding is needed because input to be encrypted must be a multiple of the block size (DES:8-bytes, AES:16-bytes). If the input is not **always** a multiple the input must be padded prior to encryption and the padding removed after decryption. There are modes that do not need padding but ECB and CBC do. – zaph Nov 21 '17 at 19:22
  • thanks for explanation I got what you mean, I have applied padding (in presented source code you may see it) and I sure that it works because bb10 can decode what it encoded before, and the same for PHP it can decode what it encoded before. I suppose that there is issue with key side, for some reason BB10 restrict key side to 8-bytes (while PHP use 24-bytes key), and I didn't managed to find a way to change it – CAMOBAP Nov 21 '17 at 20:15
  • These BB lines take a 8-byte key and create a 24-byte key: `$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);`. You need to do the same thing, ` – zaph Nov 21 '17 at 20:31
  • BB stands for BlackBerry, all blackberry implementation done in c++, code attached above, I understand that PHP part build 24-byte key, but BB10 crypto API doesn't accepted keys which isn't 8-byte, and return error code for any none 8-byte keys – CAMOBAP Nov 21 '17 at 21:00