CMAC Key generation with OpenSSL EVP_DigestSign* fails
Asked Answered
A

2

7

I am trying to generate key for computing the CMAC with OpenSSL.

However, these seem to fail with the error message copied below. Can someone point out where the problem is? Has anybody done CMAC with EVP_DigestSign* calls?

Here is part of the code which has been constructed from the example at https://wiki.openssl.org/index.php/EVP_Key_and_Parameter_Generation :

BIO *out = NULL;   
out = BIO_new(BIO_s_file());

if (out == NULL)
        return -1;
BIO_set_fp(out, stdout, BIO_NOCLOSE);

EVP_MD_CTX* rctx = NULL;
EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *kctx = NULL;

rctx = EVP_MD_CTX_create();
if(rctx == NULL) {
    printf("EVP_MD_CTX_create failed\n");
}
 if(!EVP_PKEY_keygen_init(kctx)){
     printf("EVP_PKEY_keygen_init failed\n");
}

if (EVP_PKEY_CTX_ctrl(kctx, -1, EVP_PKEY_OP_KEYGEN,EVP_PKEY_CTRL_CIPHER,0, (void *)EVP_aes_256_ecb()) <= 0)
    printf("EVP_PKEY_CTX_ctrl 1  failed\n");

if (EVP_PKEY_CTX_ctrl(kctx, -1, EVP_PKEY_OP_KEYGEN,EVP_PKEY_CTRL_SET_MAC_KEY,/*key length*/32, "01234567890123456789012345678901") <= 0)
    printf("Set the key data failed 1\n");

Here the error:

EVP_PKEY_CTX_ctrl failed
3073898120:error:06093096:lib(6):func(147):reason(150):pmeth_gn.c:122:
3073898120:error:06089093:lib(6):func(137):reason(147):pmeth_lib.c:390:

And at line 390 in pmeth_lib.c:

EVPerr(EVP_F_EVP_PKEY_CTX_CTRL, EVP_R_COMMAND_NOT_SUPPORTED);

I am using OpenSSL 1.0.1e.

See also How to calculate AES CMAC using OpenSSL's CMAC_xxx functions?

Arriola answered 25/1, 2016 at 10:19 Comment(1)
At the end of the "Signing" section, it mentions that CMAC is only supported in the 1.1.0 OpenSSL version: "Note that CMAC is only supported in the (as yet unreleased) version 1.1.0 of OpenSSL". However, if you don't use the EVP API, you should still be able to use CMAC; see here for a possible example.Sprue
A
2

I managed to get CMAC working using EVP interfaces. The key generation part which failed earlier also works. Here is the code. As you can see, I have taken the example posted here: How to calculate AES CMAC using OpenSSL? which uses the CMAC_Init/Update/Final interfaces of OpenSSL and tried various NIST values to check if the EVP interfaces work for CMAC: Here is the code snippet. The key generation also works now. Please let me know if there is anything that I have overlooked. I do have a remark while using EVP. Please see the remark section below as well.

/*
 * CMACSiging.c
 */

#include <stdio.h>
#include <openssl/cmac.h>
#include <openssl/err.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
#include <string.h>

typedef signed char       int8_t;
typedef signed short      int16_t;
typedef signed int        int32_t;
typedef unsigned char     uint8_t;
typedef unsigned short    uint16_t;
typedef unsigned int      uint32_t;

void printBytes(unsigned char *buf, size_t len) {
    int i;
    for(i=0; i<len; i++) {
        printf("%02x", buf[i]);
    }
    printf("\n");
}
EVP_PKEY *generate_key(int type)
{
    EVP_PKEY_CTX *pctx = NULL, *kctx = NULL;
    EVP_PKEY *params = NULL, *key = NULL;

    unsigned char k[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4};
    /* Create context for the key generation */
    if(!(kctx = EVP_PKEY_CTX_new_id(type, NULL))) goto err;

    /* Generate the key */

    if(!EVP_PKEY_keygen_init(kctx)) goto err;

    if(type == EVP_PKEY_CMAC)
    {
        if (EVP_PKEY_CTX_ctrl(kctx, -1, EVP_PKEY_OP_KEYGEN,
            EVP_PKEY_CTRL_CIPHER,
            0, (void *)EVP_aes_256_cbc()) <= 0)
            goto err;

        if (EVP_PKEY_CTX_ctrl(kctx, -1, EVP_PKEY_OP_KEYGEN,
            EVP_PKEY_CTRL_SET_MAC_KEY, sizeof(k), k) <= 0)
            goto err;
    }

    if (!EVP_PKEY_keygen(kctx, &key)) goto err;

   goto end;
err:

end:

    if(pctx) EVP_PKEY_CTX_free(pctx);
    if(params) EVP_PKEY_free(params);
    if(kctx) EVP_PKEY_CTX_free(kctx);

    return key;
}

void trial(uint8_t *msg, uint8_t mlen, uint8_t *key, uint8_t keylen, uint8_t **sig, uint8_t *slen)
{
//16 byte msg with 32 byte key with aes 256 cbc based CMAC
    if(!msg || !mlen || !key) {
        //handleError
    }

    if(*sig)
        OPENSSL_free(*sig);

    *sig = NULL;
    *slen = 0;

    EVP_MD_CTX* ctx = NULL;
    EVP_PKEY *pkey = NULL;
    const EVP_MD* md = NULL;
    OpenSSL_add_all_digests();

    do
    {
        ctx = EVP_MD_CTX_create();
        if(ctx == NULL) {
            printf("EVP_MD_CTX_create failed\n");
            break; // failed
        }
        if(!(md = EVP_get_digestbyname("SHA256")))
                printf("EVP_get_digestbyname failed\n");

        printf("Over to EVP calls \n");
        if(!(pkey = generate_key(EVP_PKEY_CMAC))) printf("Error 5 \n");
        int rc ;
        rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
        if(rc != 1) {
            printf("EVP_DigestSignInit failed\n");
            ERR_print_errors_fp(stdout);
            break;
        }

        rc = EVP_DigestSignUpdate(ctx, msg, mlen);
        if(rc != 1) {
            printf("EVP_DigestSignUpdate failed\n");
            ERR_print_errors_fp(stdout);
            break;
        }

        size_t req = 0;
        rc = EVP_DigestSignFinal(ctx, NULL, &req);

        if(rc != 1) {
            printf("EVP_DigestSignFinal failed\n");
            ERR_print_errors_fp(stdout);
            break;
        }

        if(!(req > 0)) {
            printf("EVP_DigestSignFinal failed (2)\n");
            break;
        }

        *sig = OPENSSL_malloc(req);
        if(*sig == NULL) {
            printf("OPENSSL_malloc failed, error \n");
            break;
        }

        *slen = req;
        rc = EVP_DigestSignFinal(ctx, *sig, slen);
        if(rc != 1) {
            printf("EVP_DigestSignFinal failed (3)\n");
            ERR_print_errors_fp(stdout);
            break;
        }

    } while(0);

    if(ctx) {
        EVP_MD_CTX_destroy(ctx);
        ctx = NULL;
    }
}

int main(int argc, char *argv[])
{
    // https://tools.ietf.org/html/rfc4493

    // K, M and T from
    // http://csrc.nist.gov/publications/nistpubs/800-38B/Updated_CMAC_Examples.pdf
    // D.1 AES-128

    // K: 2b7e1516 28aed2a6 abf71588 09cf4f3c

    unsigned char key[] = {0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4};

    // M: 6bc1bee2 2e409f96 e93d7e11 7393172a Mlen: 128
    unsigned char message[] = { 0x6b,0xc1,0xbe,0xe2,
            0x2e,0x40,0x9f,0x96,
            0xe9,0x3d,0x7e,0x11,
            0x73,0x93,0x17,0x2a };

    unsigned char mact[16] = {0};
    size_t mactlen;

    CMAC_CTX *ctx = CMAC_CTX_new();
    CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL);
    printf("message length = %lu bytes (%lu bits)\n",sizeof(message), sizeof(message)*8);

    CMAC_Update(ctx, message, sizeof(message));
    CMAC_Final(ctx, mact, &mactlen);

    printBytes(mact, mactlen);
    //expected result T = 070a16b4 6b4d4144 f79bdd9d d04a287c

    CMAC_CTX_free(ctx);


    uint8_t key_len = sizeof(key);
    uint8_t mlen = sizeof(message);
    uint8_t *dgst = NULL;
    size_t dlen;
    trial( message, mlen, key, key_len, &dgst, &dlen);
    printf("length of sig = %d\n", dlen);
    printf("CMAC returned from trial is: ");
    int i;
    for(i = 0; i < dlen; i++)
        printf("%02x", dgst[i]);
    printf("\n");

    return 0;
}

REMARK:

As seen in code, what is important is to note that when using EVP_DigestSignInit, the message digest envelope structure (EVP_MD) should not be taken as NULL as mentioned in the OpenSSL Wiki (ATLEAST for OpenSSL 1.0.2e which is where I tested):

Note: There is no difference in the API between signing using an asymmetric algorithm, and generating a MAC code. In the case of CMAC no message digest function is required (NULL can be passed). Signing using the EVP_Sign* functions is very similar to the above example, except there is no support for MAC codes. Note that CMAC is only supported in the (as yet unreleased) version 1.1.0 of OpenSSL.

If NULL is passed, I get an error that the digest is NULL. It seems like what is passed doesn't affect the CMAC generation as this struct is basically ignored. I hope the OpenSSL guys out here can clarify. Thanks!!

Arriola answered 4/2, 2016 at 10:52 Comment(0)
T
1

It's currently impossible because it's not supported.

Only thing that you can try is to use the latest OpenSSL and try the same thing, but I doubt it would work. Try without EVP.

Tartarean answered 3/2, 2016 at 10:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.