Я хочу вычислить CMAC с использованием OpenSSL. Я нашел этот вопрос, который мне помог.

Но я столкнулся с проблемой со следующим кодом:

#include <openssl/cmac.h>

void dispHex(const unsigned char *buffer, unsigned int size) {
    int i=0;

    for (i=0; i<size-1; i++) {
        printf("%02X ", buffer[i]);
    }
    printf("%02x\n", buffer[i]);
}

int main() {
    size_t out_len;
    unsigned char res[16];

    unsigned char mac_key[16] = { 0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07,
                            0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07};

    unsigned char msg[16] = { 0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07,
                            0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07};

    CMAC_CTX *cmac = CMAC_CTX_new();
    CMAC_Init(cmac, mac_key, 16, EVP_aes_128_cbc(), NULL);
    CMAC_Update(cmac, msg, sizeof(msg));
    CMAC_Final(cmac, res, &out_len);
    dispHex(res, sizeof(res));

    return 0;
}

Я компилирую его с помощью gcc -o test_cmac test_cmac_openssl.c -L C:/OpenSSL-Win32 -llibeay32 -I C:/OpenSSL-Win32/include, и он без проблем создает test_cmac.exe.

Но когда я его запускаю (./test_cmac.exe), ничего не происходит. Он просто печатает пустую строку и останавливается:

xxx@DESKTOP /cygdrive/e/
$ ./test_cmac.exe

xx@DESKTOP /cygdrive/e/

Даже если я добавлю printf("..."); перед вычислением CMAC, все будет так же.

Что странно, так это программа:

#include <openssl/hmac.h>

void dispHex(const unsigned char *buffer, unsigned int size) {
    int i=0;

    for (i=0; i<size-1; i++) {
        printf("%02X ", buffer[i]);
    }
    printf("%02X\n", buffer[i]);
}

int main() {
    size_t out_len;
    unsigned char res[32];

    unsigned char mac_key[16] = { 0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07,
                            0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07};

    unsigned char msg[16] = { 0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07,
                            0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07};

    HMAC_CTX hmac;
    HMAC_CTX_init(&hmac);
    HMAC_Init_ex(&hmac, mac_key, 16, EVP_sha256(), NULL);
    HMAC_Update(&hmac, msg, sizeof(msg));
    HMAC_Final(&hmac, res, &out_len);
    dispHex(res, sizeof(res));

    return 0;

}

Работает нормально: после gcc -o test_hmac test_hmac_openssl.c -L C:/OpenSSL-Win32 -llibeay32 -I C:/OpenSSL-Win32/include и ./test_hmac.exe я получаю:

xxx@DESKTOP /cygdrive/e/
$ ./test_hmac.exe
...9A 21 F8 2D 60 84 6C 09 08 98 A5 1F 23 8C C8 8F C4 A9 0C C4 49 45 DA 10 B9 39 C0 93 C3 10 60 BE

xxx@DESKTOP /cygdrive/e/

Так что я немного запутался ... Почему он работает с примитивом HMAC, а не с CMAC? Кто-нибудь уже сталкивался с подобными проблемами?

Я использую 32-битную версию OpenSSL: openssl version возвращает OpenSSL 1.0.2e 3 Dec 2015. Я также проверил, что C:\OpenSSL-Win32\ объявлен в переменной окружения PATH.

2
Raoul722 19 Фев 2016 в 14:36

2 ответа

Лучший ответ

Почему он работает с примитивом HMAC, а с CMAC - нет? Кто-нибудь уже сталкивался с подобной проблемой?

Я предполагаю, что большинство проблем связано с смешиванием и сопоставлением компиляторов. Win32 OpenSSL от Shining Light использует компилятор Microsoft, а вы используете GCC. Вероятно, также существует некоторое смешение и согласование сред выполнения.

Я немного удивлен, что код HMAC в конфигурации работал так, как ожидалось. Думаю, тебе повезло.

У меня работает следующее, но есть некоторые отличия:

#include <openssl/evp.h>
#include <openssl/cmac.h>

#include <stdio.h>
#include <string.h>
#include <assert.h>

void dispHex(const unsigned char *buffer, unsigned int size) {
    unsigned int i=0;

    for (i=0; i<size; i++) {
        printf("%02X ", buffer[i]);
    }
    printf("\n");
}

int main() {
    int rc = 0;
    size_t out_len = 0;
    unsigned char res[EVP_MAX_MD_SIZE];

    unsigned char mac_key[16] = { 0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07,
                            0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07};

    unsigned char msg[16] = { 0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07,
                            0x00, 0x01 ,0x02 ,0x03, 0x04, 0x05, 0x06, 0x07};

    CMAC_CTX *cmac = CMAC_CTX_new();
    assert(cmac != NULL);

    rc = CMAC_Init(cmac, mac_key, sizeof(mac_key), EVP_aes_128_cbc(), NULL);
    assert(rc == 1);

    rc = CMAC_Update(cmac, msg, sizeof(msg));
    assert(rc == 1);

    rc = CMAC_Final(cmac, res, &out_len);
    assert(rc == 1);

    dispHex(res, out_len);

    CMAC_CTX_free(cmac);

    return 0;
}

Отличия вашей конфигурации от той, которую я использовал:

  1. Один и тот же компилятор используется как для OpenSSL, так и для тестовой программы.
  2. res по размеру EVP_MAX_MD_SIZE
  3. CMAC_Init использует sizeof(mac_key)
  4. out_len инициализирован
  5. displayHex использует out_len, а не sizeof(res)

Использование sizeof(res) распечатает 16 байтов MAC, а затем распечатает 16 мусорных символов, потому что res объявлен как unsigned char res[32]. Не думаю, что вы зашли так далеко, так что имейте это в виду.


Программа дает следующий результат. Я не знаю, является ли это исправленным / ожидаемым результатом, но он дает результат и печатает его:

$ ./test.exe 
43 91 63 0E 47 4E 75 A6 2D 95 7A 04 1A E8 CC CC 

OpenSSL не поддерживает Cygwin-x64, поэтому вам нужно будет использовать версию i686. См. Также Проблема № 4326: Не удалось настроить для Cygwin-x64 в системе отслеживания ошибок OpenSSL.

Вот как собрать OpenSSL под Cygwin-x64. Скрипт сборки OpenSSL имеет несколько изгибов, поэтому вы не можете использовать config . Вы должны использовать Configure и вызвать тройку. Проблема с отслеживанием ошибок все еще актуальна, потому что config должен все исправить.

$ curl https://www.openssl.org/source/openssl-1.0.2f.tar.gz -o openssl-1.0.2f.tar.gz
...
$ tar -xzf openssl-1.0.2f.tar.gz
...
$ cd openssl-1.0.2f

Затем:

$ export KERNEL_BITS=64
$ ./Configure Cygwin-x86_64 shared no-ssl2 no-ssl3 --openssldir="$HOME/ssl"
...
$ make depend
...
$ make
...
$ make install_sw

install_sw устанавливает заголовки в $OPENSSLDIR/include, а библиотеки в $OPENSSLDIR/lib. Он не устанавливает страницы руководства.

Затем вы компилируете и связываете с:

$ gcc -I "$HOME/ssl/include" test.c -o test.exe "$HOME/ssl/lib/libcrypto.a"

Связывание с libcrypto.a означает, что вы избегаете проблем с путями к библиотеке. У вас все будет "просто работать".

1
Community 23 Май 2017 в 12:31

Глядя на CMAC_Init, кажется, что инициализация объекта CMAC не выполняется, потому что IMPL равен NULL:

int CMAC_Init(CMAC_CTX *ctx, const void *key, size_t  const EVP_CIPHER *cipher, ENGINE *IMPL)
if (!key && !cipher && !IMPL && keylen == 0) {
    /* Not initialised */
    if (ctx->nlast_block == -1)
        return 0;
    if (!EVP_EncryptInit_ex(ctx->cctx, NULL, NULL, NULL, zero_iv))
        return 0;
    memset(ctx->tbl, 0, EVP_CIPHER_CTX_block_size(ctx->cctx));
    ctx->nlast_block = 0;
    return 1;
}
2
jww 20 Фев 2016 в 23:01