4

The code below generates a signed hash using HMAC SHA256. This code compiles and works fine on Debian Jessie and Ubuntu 16.04 (OpenSSL 1.0.2g 1 Mar 2016).

#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <iomanip>
#include <iostream>
#include <string>
#include <sstream>

using namespace std;

string HMAC256(string data, string key)
{
        stringstream ss;
        HMAC_CTX ctx;
        unsigned int  len;
        unsigned char out[EVP_MAX_MD_SIZE];
        HMAC_Init(&ctx, key.c_str(), key.length(), EVP_sha256());
        HMAC_Update(&ctx, (unsigned char*)data.c_str(), data.length());
        HMAC_Final(&ctx, out, &len);
        HMAC_cleanup(&ctx); 
        for (unsigned int i = 0;  i < len;  i++)
        {
          ss << setw(2) << setfill('0') << hex << static_cast<int> (out[i]);
        }
        return ss.str();
}

int main()
{
    cout << HMAC256("AAAA","BBBB") << endl;
    return 0;
}

HOWEVER....

When compiling it on Debian Stretch I get the following error:

hmac256.cpp: In function ‘std::__cxx11::string HMAC256(std::__cxx11::string, std::__cxx11::string)’:
hmac256.cpp:14:18: error: aggregate ‘HMAC_CTX ctx’ has incomplete type and cannot be defined
         HMAC_CTX ctx;
                  ^~~
hmac256.cpp:18:9: warning: ‘int HMAC_Init(HMAC_CTX*, const void*, int, const EVP_MD*)’ is deprecated [-Wdeprecated-declarations]
         HMAC_Init(&ctx, key.c_str(), key.length(), EVP_sha256());
         ^~~~~~~~~
In file included from /usr/include/openssl/hmac.h:13:0,
                 from hmac256.cpp:2:
/usr/include/openssl/hmac.h:28:1: note: declared here
 DEPRECATEDIN_1_1_0(__owur int HMAC_Init(HMAC_CTX *ctx, const void *key, int len,
 ^

And this has to do with the new OpenSSL version (OpenSSL 1.1.0f 25 May 2017).

QUESTION

Why am I experiencing the problem with OpenSSL 1.1, and how to fix it in a way that maintains backward compatibility with OpenSSL 1.0?

jww
  • 97,681
  • 90
  • 411
  • 885
TTKDroid
  • 231
  • 4
  • 12
  • 2
    See [OpenSSL 1.1.0 Changes](https://wiki.openssl.org/index.php/OpenSSL_1.1.0_Changes) on the OpenSSL wiki (in particular, see the Compatibility Layer stuff). Also see [Error: incomplete type when using HMAC_CTX in C++ project](https://stackoverflow.com/q/40553796/608639), [Error: “invalid use of incomplete type ‘RSA {aka struct rsa_st}” in OpenSSL 1.1.0](https://stackoverflow.com/q/40549318/608639), [Error “incomplete type MD5_CONTEXT” with MariaDB 10.2 and Openssl 1.1.0e](https://stackoverflow.com/q/44012487/608639), etc on Stack Overflow. – jww Jun 27 '17 at 03:15
  • You should probably just use the EVP interfaces. They appear to be stable across OpenSSL 1.0.2 and OpenSSL 1.1.0. For that, see [EVP Signing and Verifying](https://wiki.openssl.org/index.php/EVP_Signing_and_Verifying) on the OpenSSL wiki. And you might also be interested in [EVP Symmetric Encryption and Decryption | C++ Programs](https://wiki.openssl.org/index.php/EVP_Symmetric_Encryption_and_Decryption#C.2B.2B_Programs). It offers some tricks when working with C++. – jww Jun 27 '17 at 03:16

1 Answers1

5

For fixing the error, please read: Upgrade To OpenSSL 1.1.0. Basically, you need to create a new HMAC_CTX as follows:

HMAC_CTX *h = HMAC_CTX_new();
HMAC_Init_ex(h, key, keylen, EVP_sha256(), NULL);
...
HMAC_CTX_free(h);

For backward compatibility, you can consider using macros to control the code block to compile.

douyw
  • 4,034
  • 1
  • 30
  • 28