-2

I have this code:

string HMACsha256(string message, string APIkey){

    // The secret key for hashing
    char key[APIkey.length() + 1];
    strcpy(key, APIkey.c_str());

    // The data that we're going to hash
    char data[message.length() + 1];
    strcpy(data, message.c_str());

    unsigned char* result;
    unsigned int len = 64;

    result = (unsigned char*)malloc(sizeof(char) * len);

    HMAC_CTX ctx;
    HMAC_CTX_init(&ctx);

    HMAC_Init_ex(&ctx, key, strlen(key), EVP_sha256(), NULL);
    HMAC_Update(&ctx, (unsigned char*)&data, strlen(data));
    HMAC_Final(&ctx, result, &len);
    HMAC_CTX_cleanup(&ctx);

    printf("HMAC digest: ");

    string signature; // <-------- I want to store the values from the printf with result in the loop below in this

    for (int i = 0; i != len; i++){
        printf("%02x", (unsigned int)result[i]); //<--- I want this in signature string
    }

    cout << endl<<  "SIG: " << signature << endl;

    free(result);

    return signature;
}

Everything about it works fine, except I need to be able to return the result as a string. I've been unable to come up with a way to take the hex data from the unsigned char* result variable and store it into the signature string. Does anyone know how I can go about this?

EDIT: This question was flagged as a duplicate of convert a char* to std::string However, I have already tried the excepted answer on that thread of doing

const char *s = "Hello, World!";
std::string str(s);

With my code it looks like:

string signature(result);

Running my code like that produces this error:

error: no matching constructor for initialization of 'string' (aka 'basic_string<char, char_traits<char>, allocator<char> >')
    string signature(result);

With

 string signature(reinterpret_cast<char*>(result));

The results are:

 Result:
 20ef96358c199befc0a0e6de47170734532e48e7ddfbf4ea48ef207989342677
 Signature:
 ?5???????G4S.H?????H? y?4&w?Ήf?

It's been brought to my attention that I "don't want to convert the data to string as is, but rather a hexadecimal representation" if that makes this question more clear for anyone.

runesbane
  • 75
  • 7
  • Yes. I've tried the solution I just edited in above, string stream, and assign. Either a previously posted solution doesn't compile with my code, or it produces garbage random values as the result. – runesbane Apr 04 '18 at 13:52
  • Does any of the functions you pass `result` to add the null-terminator at the end? Otherwise try `return std::string(result, result + len);`. – Bob__ Apr 04 '18 at 13:57
  • 3
    @runesbane sorry, I didn't notice `unsigned`, that was important. See this duplicate instead: https://stackoverflow.com/questions/17746688/convert-unsigned-char-to-string – eerorika Apr 04 '18 at 13:57
  • 1
    aside: since len is a compile time constant; you may prefer to use an array; std::array; or std::vector to hold your characters. so that you don't have the leak if you throw an exception before your free – UKMonkey Apr 04 '18 at 14:00
  • @user2079303 I tried that solution as well, It produces garbage results in signature: HMAC digest: 20ef96358c199befc0a0e6de47170734532e48e7ddfbf4ea48ef207989342677 <---- this is what result is ?5???????G4S.H?????H? y?4&w?Ήf? <---- this is what gets sent to signature – runesbane Apr 04 '18 at 14:03
  • @Bob__ while your suggestion does compile it also produces garbage values in signature string...I'm really not sure what the issue is here but it's going right over my head whatever it is. – runesbane Apr 04 '18 at 14:07
  • @runesbane Ah, so you don't want to convert the data to string *as is*, but rather a hexadecimal representation? You should elaborate on that in the question. – eerorika Apr 04 '18 at 14:08
  • @user2079303 I guess so? I really wasn't aware that was what was needed to be done or I would've mentioned it. – runesbane Apr 04 '18 at 14:09
  • There's also no need to copy `APIkey` into a non standard VLA, just pass `APIkey.c_str()` to the init function. – Bob__ Apr 04 '18 at 14:16

1 Answers1

0

This code does too many unnecessary copies and calls.

A simpler version:

inline char binary_to_hex_digit(unsigned a) {
    return a + (a < 10 ? '0' : 'a' - 10);
}

std::string binary_to_hex(unsigned char const* binary, unsigned binary_len) {
    std::string r(binary_len * 2, '\0');
    for(unsigned i = 0; i < binary_len; ++i) {
        r[i * 2] = binary_to_hex_digit(binary[i] >> 4);
        r[i * 2 + 1] = binary_to_hex_digit(binary[i] & 15);
    }
    return r;
}

std::string HMACsha256(std::string const& message, std::string const& key) {
    unsigned char result[EVP_MAX_MD_SIZE];
    unsigned result_len = 0;
    HMAC(EVP_sha256(), key.data(), key.size(), reinterpret_cast<unsigned char const*>(message.data()), message.size(), result, &result_len);
    return binary_to_hex(result, result_len);
}

int main() {
    auto mac = HMACsha256("message", "password");
    std::cout << mac << '\n';
}
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271