25

I need an example of how to use Crypto++ to generate a SHA256 hash from a std::string and output a std::string. I can't seem to figure it out. Everything I've tried gives me invalid output.

Here's the new code after interjay's answer:

string SHA256(string data)
{
    byte const* pbData = (byte*) data.data();
    unsigned int nDataLen = data.size();
    byte abDigest[CryptoPP::SHA256::DIGESTSIZE];

    CryptoPP::SHA256().CalculateDigest(abDigest, pbData, nDataLen);

    return string((char*)abDigest);
}

The output for SHA256("A"); is

enter image description here

How can I turn this into a readable format?

Thanks to interjay's answer I was able to generate the final hash.

jww
  • 97,681
  • 90
  • 411
  • 885
Doug
  • 935
  • 2
  • 10
  • 15

3 Answers3

25

This outputs a base64 string using the CryptoPP::Base64Encoder:

#include "sha.h"
#include "filters.h"
#include "base64.h"

std::string SHA256HashString(std::string aString){
    std::string digest;
    CryptoPP::SHA256 hash;

    CryptoPP::StringSource foo(aString, true,
    new CryptoPP::HashFilter(hash,
      new CryptoPP::Base64Encoder (
         new CryptoPP::StringSink(digest))));

    return digest;
}
Twonky
  • 796
  • 13
  • 31
Rooke
  • 2,013
  • 3
  • 22
  • 34
  • Fantastic, I was looking for a way to do this with pipelining! – Ethan Apr 11 '16 at 23:55
  • CryptoPP also uses [SIMD x64](https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions) instructions that speed it up. – ahmd0 Jul 13 '17 at 07:39
  • 1
    Hi, I saw similar code elsewhere. I wonder why an explicit delete isn't needed for the HashFilter, Base64Encoder and StringSink? Thanks in advance! – Hei Mar 23 '18 at 05:07
  • @Hei: yeah, looks like a bunch of memory leaks. Or, is the author a fan of Java. :) – MikeF Mar 14 '19 at 04:49
  • 1
    No, this does not leak memory. This is idiomatic CyptoPP code using https://www.cryptopp.com/wiki/Pipelining – Rooke Apr 26 '20 at 14:00
  • Very good answer but more usual to output as hex digits rather than base64 – Arthur Tacca May 15 '20 at 10:56
20

This line will give the wrong results:

unsigned int nDataLen = sizeof(pbData);

It will always give you the size of a pointer. What you want instead is data.size().

Also, you don't need this part:

if(!CryptoPP::SHA256().VerifyDigest(abDigest, pbData, nDataLen))
{
    return SHA256(data);
}

It should always verify correctly, since you just calculated the digest based on the same data. And if it didn't, you'd go into infinite recursion.

To get readable output, you can convert it to hex. Here's an example for MD5 from the Crypto++ Wiki, it should work for you if you replace MD5 with SHA256:

CryptoPP::MD5 hash;
byte digest[ CryptoPP::MD5::DIGESTSIZE ];
std::string message = "abcdefghijklmnopqrstuvwxyz";

hash.CalculateDigest( digest, (byte*) message.c_str(), message.length() );

CryptoPP::HexEncoder encoder;
std::string output;
encoder.Attach( new CryptoPP::StringSink( output ) );
encoder.Put( digest, sizeof(digest) );
encoder.MessageEnd();

std::cout << output << std::endl;  
interjay
  • 107,303
  • 21
  • 270
  • 254
  • Okay I made those changes and while it now seems to be giving me the same output each time, the output is in all gibberish of random weird characters. How can I change it to a readable format? – Doug May 08 '11 at 20:39
  • Applying your example gives a conversion error from `const char*` to `const byte*`. `(byte*) message.c_str()` is the right conversion. – 3isenHeim Sep 11 '15 at 09:49
4

Your code will expect a null-terminated string from the buffer you supply to the string constructor! Which means the result will almost certainly be wrong.

To enforce the digest size and, use the following instead:

return std::string((char*)abDigest, CryptoPP::SHA256::DIGESTSIZE);

Also with respect to printing it the following correctly produces the test vector BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD for the string "abc"

std::string string_to_hex(const std::string& input)
{
  static const char* const lut = "0123456789ABCDEF";
  size_t len = input.length();

  std::string output;
  output.reserve(2 * len);
  for (size_t i = 0; i < len; ++i)
  {
    const unsigned char c = input[i];
    output.push_back(lut[c >> 4]);
    output.push_back(lut[c & 15]);
  }
  return output;
}

std::string SHA256(std::string data)
{
  CryptoPP::byte const* pbData = (CryptoPP::byte*)data.data();
  unsigned int nDataLen = data.length();
  CryptoPP::byte abDigest[CryptoPP::SHA256::DIGESTSIZE];

  CryptoPP::SHA256().CalculateDigest(abDigest, pbData, nDataLen);

  // return string((char*)abDigest);  -- BAD!!!
  return std::string((char*)abDigest, CryptoPP::SHA256::DIGESTSIZE);
}

void test_cryptopp() {
  std::cout << string_to_hex(SHA256("abc")) << std::endl;
}
asenski
  • 41
  • 2