Mahadeva
Mahadeva

Reputation: 1637

OpenSSL SHA256 Wrong result

I have following piece of code that is supposed to calculate the SHA256 of a file. I am reading the file chunk by chunk and using EVP_DigestUpdate for the chunk. When I test the code with the file that has content

Test Message Hello World

in Windows, it gives me SHA256 value of 97b2bc0cd1c3849436c6532d9c8de85456e1ce926d1e872a1e9b76a33183655f but the value is supposed to be 318b20b83a6730b928c46163a2a1cefee4466132731c95c39613acb547ccb715, which can be verified here too.

Here is the code:

#include <openssl\evp.h>
#include <iostream>
#include <string>
#include <fstream>
#include <cstdio>
const int MAX_BUFFER_SIZE = 1024;
std::string FileChecksum(std::string, std::string);
int main()
{
    std::string checksum = FileChecksum("C:\\Users\\Dell\\Downloads\\somefile.txt","sha256");
    std::cout << checksum << std::endl;
    return 0;
}

std::string FileChecksum(std::string file_path, std::string algorithm)
{
     EVP_MD_CTX *mdctx;
     const EVP_MD *md;
     unsigned char md_value[EVP_MAX_MD_SIZE];
     int i;
     unsigned int md_len;

     OpenSSL_add_all_digests();
     md = EVP_get_digestbyname(algorithm.c_str());

     if(!md) {
            printf("Unknown message digest %s\n",algorithm);
            exit(1);
     }

     mdctx = EVP_MD_CTX_create();
     std::ifstream readfile(file_path,std::ifstream::in|std::ifstream::binary);
     if(!readfile.is_open())
     {
         std::cout << "COuldnot open file\n";
         return 0;
     }
     readfile.seekg(0, std::ios::end);
     long filelen = readfile.tellg();
     std::cout << "LEN IS " << filelen << std::endl;
     readfile.seekg(0, std::ios::beg);
     if(filelen == -1)
     {
         std::cout << "Return Null \n";
         return 0;
     }

     EVP_DigestInit_ex(mdctx, md, NULL);
     long temp_fil = filelen;
     while(!readfile.eof() && readfile.is_open() && temp_fil>0)
     {

         int bufferS = (temp_fil < MAX_BUFFER_SIZE) ? temp_fil : MAX_BUFFER_SIZE;
         char *buffer = new char[bufferS+1];
         buffer[bufferS] = 0;
         readfile.read(buffer, bufferS);
         std::cout << strlen(buffer) << std::endl;
         EVP_DigestUpdate(mdctx, buffer, strlen(buffer));
         temp_fil -= bufferS;
         delete[] buffer;
     }
     EVP_DigestFinal_ex(mdctx, md_value, &md_len);
     EVP_MD_CTX_destroy(mdctx);

     printf("Digest is: ");
     //char *checksum_msg = new char[md_len];
     //int cx(0);
     for(i = 0; i < md_len; i++)
     {
        //_snprintf(checksum_msg+cx,md_len-cx,"%02x",md_value[i]);
         printf("%02x", md_value[i]);
     }
     //std::string res(checksum_msg);
     //delete[] checksum_msg;

     printf("\n");

     /* Call this once before exit. */
     EVP_cleanup();
     return "";
}

I tried to write the hash generated by program as string using _snprintf but it didn't worked. How can I generate the correct hash and return the value as string from FileChecksum Function? Platform is Windows.

EDIT: It seems the problem was because of CRLF issue. As Windows in saving file using \r\n, the Checksum calculated was different. How to handle this?

Upvotes: 0

Views: 2189

Answers (2)

Mahadeva
Mahadeva

Reputation: 1637

It seems the problem was associated with the value of length I passed in EVP_DigestUpdate. I had passed value from strlen, but replacing it with bufferS did fixed the issue. The code was modified as:

while(!readfile.eof() && readfile.is_open() && temp_fil>0)
{
  int bufferS = (temp_fil < MAX_BUFFER_SIZE) ? temp_fil : MAX_BUFFER_SIZE;
  char *buffer = new char[bufferS+1];
  buffer[bufferS] = 0;
  readfile.read(buffer, bufferS);
  EVP_DigestUpdate(mdctx, buffer, bufferS);
  temp_fil -= bufferS;
  delete[] buffer;
}

and to send the checksum string, I modified the code as:

EVP_DigestFinal_ex(mdctx, md_value, &md_len);
EVP_MD_CTX_destroy(mdctx);
char str[128] = { 0 };
char *ptr = str;
std::string ret;
for(i = 0; i < md_len; i++)
 {
    //_snprintf(checksum_msg+cx,md_len-cx,"%02x",md_value[i]);
     sprintf(ptr,"%02x", md_value[i]);
     ptr += 2;
 }

ret = str;
/* Call this once before exit. */
EVP_cleanup();
return ret;

As for the wrong checksum earlier, the problem was associated in how windows keeps the line feed. As suggested by Zangetsu, Windows was making text file as CRLF, but linux and the site I mentioned earlier was using LF. Thus there was difference in the checksum value. For files other than text, eg dll the code now computes correct checksum as string

Upvotes: 0

Zangetsu
Zangetsu

Reputation: 2015

MS-DOS used the CR-LF convention,So basically while saving the file in windows, \r\n comes in effect for carriage return and newline. And while testing on online (given by you), only \n character comes in effect. Thus either you have to check the checksum of Test Message\r\nHello World\r\n in string which is equivalent to creating and reading file in windows(as given above), which is the case here.

However, the checksum of files,wherever created, will be same.

Note: your code works fine :)

Upvotes: 0

Related Questions