Reputation: 3930
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
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