yak
yak

Reputation: 3930

MD4 hash with OpenSSL, and save the result into a char array

I wrote a simple example with OpenSSL in C. I wanted to compute the MD4 hash value from my message, but I want to save result into a char array. Here’s my code with comments which will help you understand what I want to achieve:

#include <string.h>
#include <openssl/md4.h>
#include <stdio.h>

int main()
{
    unsigned char digest[MD4_DIGEST_LENGTH];
    char string[] = "hello world";

    // Run md4 for my message
    MD4((unsigned char*)&string, strlen(string), (unsigned char*)&digest);

    // Save md4 result into a char array. It doesn’t work
    char test[MD4_DIGEST_LENGTH];
    sprintf(test, "%02x", (unsigned int)digest);
    for(int i = 0; i < MD4_DIGEST_LENGTH; i++)
        printf("%02x", test[i]);
    printf("\n\n");

    // Print out md4 result. It works, but it's not into the char array as I wanted it to be
    for(int i = 0; i < MD4_DIGEST_LENGTH; i++)
        printf("%02x", digest[i]);
    printf("\n\n");

    // It works, but I don’t understand why 'mdString' is 33 size
    char mdString[33];
    for(int i = 0; i < MD4_DIGEST_LENGTH; i++)
    // And I also don’t get i*2 in this loop
         sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]);
    printf("md4 digest: %s\n", mdString);

    return 0;
}

Why does this code below not work? It shows a different MD4 value than it should be:

char test[MD4_DIGEST_LENGTH];

sprintf(test, "%02x", (unsigned int)digest);
for(int i = 0; i < MD4_DIGEST_LENGTH; i++)
    printf("%02x", test[i]);
printf("\n\n");

And how can I know what size should be mdString and why is there i*2 in the last loop?

Upvotes: 3

Views: 2118

Answers (1)

easuter
easuter

Reputation: 1197

Firstly, your call to MD4() provides an incorrect address for the string and digest arrays: by using &, you are getting the array's address (char **), not the address of the first character. Since you are explicitly casting &string and &digest to unsigned char*, the compiler won't warn you. Remove the casts, and you will receive this warning:

warning: passing argument 1 of 'MD4' from incompatible pointer type

So instead call MD4() this way:

MD4(string, strlen(string), digest);

I personally prefer to avoid explicitly casting pointers unless it is really necessary, that way you will catch incorrect type casting much more easily.

Next, you attempt to use sprintf() to convert digest to a hexadecimal integer: sprintf(test, "%02x", (unsigned int)digest);. Two things wrong here: (a) since digest is essentially a character pointer, ie: memory address, you're turning this address into an unsigned integer and then turning that integer into a hex; (b) you need to loop over the elements of digest and convert each one into a character, snprintf won't do this for you!

I see that you may be relatively new to C given the mistakes made, but don't dispair, making mistakes is the way to learn! :)

If you can afford the book, I highly recommend "C Primer Plus" by Stephen Prata. It's a great intro for anyone starting out programming and it's a very complete reference for later use when you are already comfortable with the language. Otherwise, there is plenty of material online, and googling "C pointer tutorial" will return several useful results.

Hope this helps!


EDIT:

Forgot to comment about the other snippet of code that does work, but uses 33 bytes to store the string-ized MD4 hash:

// works but i dont understand why 'mdString' is 33 size
char mdString[33];
for(int i = 0; i < MD4_DIGEST_LENGTH; i++)
// and I also dont get i*2 in this loop
     sprintf(&mdString[i*2], "%02x", (unsigned int)digest[i]);
printf("md4 digest: %s\n", mdString);

The openssl manpage for MD4() states that the hash is 16 bytes long. Knowing this, and the fact that each unsigned char can hold a value from 0 to 255, then the maximum hexadecimal representation for any individual element in digest is 0xFF, in other words, 2 ASCII characters per unsigned char.

The reason the size for msString (33) appears cryptic is because MD4_DIGEST_LENGTH should have been used to calculate the size of the array: you need 2 characters to represent each one of the elements in digest + 1 null terminator ('\0') to end the string:

char mdString[(MD4_DIGEST_LENGTH * 2) + 1];

sprintf will print 2 characters to the mdString array whenever it's fed 1 byte from digest, so you need to advance 2 index positions in mdString for each position in digest, hence the use of i * 2. The following produces the same result as using i * 2:

for(int i = 0, j = 0; i < MD4_DIGEST_LENGTH; i++, j += 2)
     sprintf(&mdString[j], "%02x", (unsigned int)digest[i]);

Upvotes: 5

Related Questions