Bitcoin Forum
December 12, 2024, 12:51:45 PM *
News: Latest Bitcoin Core release: 28.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: SHA-256 in c++ with crypto++  (Read 6871 times)
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
February 25, 2015, 08:29:12 PM
 #1

Hi

I would like to use the crypto++ librairy to create a transaction from scratch.
My first question is why this code doesn't work:

#include "crypto++/cryptlib.h"
#include "crypto++/sha.h"
#include "crypto++/hex.h"
#include <iostream>
using namespace std;

int main () {
   string myString    = string("abc");
   string myStringSHA = SHA256(myString);
   std::cout << "SHA(abc): " << myStringSHA << std::endl;
}

the output is:
SHA(abc): #x####AA@###"##a##z###a####

I have the right result with the following:

#include "crypto++/cryptlib.h"
#include "crypto++/sha.h"
#include "crypto++/hex.h"
#include <iostream>
using namespace std;

string SHA256(string);

int main () {
   CryptoPP::SHA256 hash;
    byte digest[ CryptoPP::SHA256::DIGESTSIZE ];
     string message = "abc";

    hash.CalculateDigest( digest, reinterpret_cast<byte*>(&message[0]), message.length() );
 
    CryptoPP::HexEncoder encoder;
     std::string output;
    encoder.Attach( new CryptoPP::StringSink( output ) );
        encoder.Put( digest, sizeof(digest) );
    encoder.MessageEnd();

    std::cout << " " << std::endl << "SHA(abc): " << output << std::endl;
}


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 is:
SHA(abc): BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD
which is right according to:
http://www.xorbin.com/tools/sha256-hash-calculator

Thanks
Nikinger
Full Member
***
Offline Offline

Activity: 141
Merit: 100



View Profile
February 25, 2015, 09:18:53 PM
 #2

Looks like that you forget to hex encode it.

Your result from your first try (#x####AA@###"##a##z###a####) looks like the raw byte representation of BA7816BF8F01CFEA414140DE5DAE2223B00361A396177A9CB410FF61F20015AD but with #-replaced unprintable.

Code:
.. .x .. .. .. .. .. .. .A .A .@ .. .. .. ." .# .. .. .a .. .. .. .z .. .. .. .. .a .. .. .. ..
BA 78 16 BF 8F 01 CF EA 41 41 40 DE 5D AE 22 23 B0 03 61 A3 96 17 7A 9C B4 10 FF 61 F2 00 15 AD

1EwKrY5Bn3T47r4tYqSv6mMQkUyu7hZckV
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 09, 2015, 07:04:05 PM
 #3

I made a test with this code:

Code:
int main () {
string myString    = string("abc");
string myStringSHA = SHA256(myString);
cout << "size of myStringSHA: " << myStringSHA.size() << endl;
cout << "myStringSHA: --" << myStringSHA << "---" << "--" << endl;

    for (std::size_t i = 0; i < myStringSHA.size(); ++i) {
        cout << i << ": ----" << myStringSHA[i] << "----  " << bitset<32>(myStringSHA[i]) << endl;
    }
   
    for (std::size_t i = 0; i < myStringSHA.size(); ++i) {
        bitset<8> myStringSHAbit(myStringSHA[i]);
        cout << hex << myStringSHAbit.to_ulong();
    }
    cout << endl;
    // the right answer is
    cout << "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" << endl;
}
and I got this result:

Code:
size of myStringSHA: 29
myStringSHA: --#x####AA@###"##a##z###a####--
0: ----#----  11111111111111111111111110111010
1: ----x----  00000000000000000000000001111000
2: --------  00000000000000000000000000010110
3: ----#----  11111111111111111111111110111111
4: ----#----  11111111111111111111111110001111
5: --------  00000000000000000000000000000001
6: ----##---  11111111111111111111111111001111
7: ----###--  11111111111111111111111111101010
8: ----A----  00000000000000000000000001000001
9: ----A----  00000000000000000000000001000001
10: ----@----  00000000000000000000000001000000
11: ----##---  11111111111111111111111111011110
12: ----]----  00000000000000000000000001011101
13: ----#----  11111111111111111111111110101110
14: ----"----  00000000000000000000000000100010
15: ----#----  00000000000000000000000000100011
16: ----#----  11111111111111111111111110110000
17: --------  00000000000000000000000000000011
18: ----a----  00000000000000000000000001100001
19: ----#----  11111111111111111111111110100011
20: ----#----  11111111111111111111111110010110
21: --------  00000000000000000000000000010111
22: ----z----  00000000000000000000000001111010
23: ----#----  11111111111111111111111110011100
24: ----#----  11111111111111111111111110110100
25: --------  00000000000000000000000000010000
26: ----#----  11111111111111111111111111111111
27: ----a----  00000000000000000000000001100001
28: ----####-  11111111111111111111111111110010
ba7816bf8f1cfea414140de5dae2223b0361a396177a9cb410ff61f2
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
Why the trailing "----" are reduced to 3, 2 or 1 for some characters ?
And why there are 8 missing characters in my final answer ?
hhanh00
Sr. Member
****
Offline Offline

Activity: 467
Merit: 267


View Profile
March 10, 2015, 12:29:17 AM
 #4

- The missing characters are in the "control character" range (1-1F), they are not displayed but not replaced by # either. You don't need to cast char to bitset<8>.
- The string constructor for a char* stop at the first null character because it takes a C-string. You can use string(char *, 32) or keep it as char *.

redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 10, 2015, 03:17:21 PM
 #5

Here is my last try:

Code:
int main () {
string myString    = string("abc");
string myStringSHA = SHA256(myString);
   
    for (size_t i = 0; i < myStringSHA.size(); ++i) {
        printf("%02x ", myStringSHA[i]);
    }

    cout << endl;
    // the right answer is
    cout << "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" << endl;
}

the output is
Code:
ffffffba 78 16 ffffffbf ffffff8f 01 ffffffcf ffffffea 41 41 40 ffffffde 5d ffffffae 22 23 ffffffb0 03 61 ffffffa3 ffffff96 17 7a ffffff9c ffffffb4 10 ffffffff 61 fffffff2 
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

if I remove all the trailling f and the spaces, I come with:
Code:
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f2
ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad

question 1) why do I have all those 'f' ?
question 2) the first missing character is 00 which I guess is the NULL character,
so I do I tell to c++ to interpret this character as '00' ?
CIYAM
Legendary
*
Offline Offline

Activity: 1890
Merit: 1086


Ian Knowles - CIYAM Lead Developer


View Profile WWW
March 10, 2015, 03:28:11 PM
Last edit: March 11, 2015, 06:34:22 AM by CIYAM
 #6

Let me see if I can "put you out of your misery" by creating a simple "print" function for you:

Code:
string print_hash( const string& hash )
{
   ostringstream outs;

   if( hash.length( ) == 32 )
   {
      for( size_t i = 0; i < 32; i++ )
         outs << hex << setw( 2 ) << setfill( '0' ) << ( unsigned )hash[ i ];
   }

   return outs.str( );
}

You'll need to add the #include <sstream> and #include <iomanip> but that should solve your problem.

You might want to throw an exception if the length isn't 32 (it will just return a blank string in that case as is).

With CIYAM anyone can create 100% generated C++ web applications in literally minutes.

GPG Public Key | 1ciyam3htJit1feGa26p2wQ4aw6KFTejU
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 10, 2015, 04:42:32 PM
 #7

if I use your code

Code:
    cout << print_hash(myStringSHA) << endl;
   
    for (size_t i = 0; i < myStringSHA.size(); ++i) {
        printf("%02x", myStringSHA[i]);
    }
I get exactly the same as before:
Code:
ffffffba7816ffffffbfffffff8f01ffffffcfffffffea414140ffffffde5dffffffae2223ffffffb00361ffffffa3ffffff96177affffff9cffffffb410ffffffff61fffffff2
ffffffba7816ffffffbfffffff8f01ffffffcfffffffea414140ffffffde5dffffffae2223ffffffb00361ffffffa3ffffff96177affffff9cffffffb410ffffffff61fffffff2
and the size of myStringSHA is still 29 and not 32...
CIYAM
Legendary
*
Offline Offline

Activity: 1890
Merit: 1086


Ian Knowles - CIYAM Lead Developer


View Profile WWW
March 10, 2015, 04:48:33 PM
 #8

if I use your code

Code:
    cout << print_hash(myStringSHA) << endl;
    
    for (size_t i = 0; i < myStringSHA.size(); ++i) {
        printf("%02x", myStringSHA[i]);
    }
I get exactly the same as before:
Code:
ffffffba7816ffffffbfffffff8f01ffffffcfffffffea414140ffffffde5dffffffae2223ffffffb00361ffffffa3ffffff96177affffff9cffffffb410ffffffff61fffffff2
ffffffba7816ffffffbfffffff8f01ffffffcfffffffea414140ffffffde5dffffffae2223ffffffb00361ffffffa3ffffff96177affffff9cffffffb410ffffffff61fffffff2
and the size of myStringSHA is still 29 and not 32...

So your problem is the string not the output of the string.

If you feed my function the actual "binary digest" it'll work perfectly.

With CIYAM anyone can create 100% generated C++ web applications in literally minutes.

GPG Public Key | 1ciyam3htJit1feGa26p2wQ4aw6KFTejU
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 10, 2015, 06:31:27 PM
 #9

from wikipedia:

"The input data is often called the message, and the hash value is often called the message digest or simply the digest."

so my digest is "myStringSHA" :

string myStringSHA = SHA256(myString);

but it is defined as a string. How can I convert it to "binary digest" ?
CIYAM
Legendary
*
Offline Offline

Activity: 1890
Merit: 1086


Ian Knowles - CIYAM Lead Developer


View Profile WWW
March 10, 2015, 06:34:18 PM
Last edit: March 11, 2015, 06:33:55 AM by CIYAM
 #10

The output you are getting "is the digest in binary form".

So just pass that to the function I gave you and you should see your hash in ASCII form.


With CIYAM anyone can create 100% generated C++ web applications in literally minutes.

GPG Public Key | 1ciyam3htJit1feGa26p2wQ4aw6KFTejU
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 10, 2015, 07:21:19 PM
 #11

OK, I put my output in your function and I get nothing,
The size of that output is 29. If I change 32 for 29 in your
function I have a (wrong) result. I still don't see what's wrong.
Code:
int main ()
{
string myString    = string("abc");
string myStringSHA = SHA256(myString);

cout << print_hash(myStringSHA) << endl;
}
Quote
If you are just determined to be an idiot then no-one can help you
Maybe, I spent too much time the last years, doing my PhD in physics Undecided
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 10, 2015, 08:47:05 PM
 #12

OK, Thanks a lot, I get the point.
By the way, I also appreciate your link to the ignore list...
hhanh00
Sr. Member
****
Offline Offline

Activity: 467
Merit: 267


View Profile
March 11, 2015, 12:03:03 AM
 #13

 Replace
 return string((char*)abDigest);

With   return string((char*)abDigest, 32);

String can store binary data.

hhanh00
Sr. Member
****
Offline Offline

Activity: 467
Merit: 267


View Profile
March 11, 2015, 04:48:59 AM
 #14

The output you are getting "is the digest in binary form".

So just pass that to the function I gave you and you should see your hash in ASCII form.

(delete your fucking code and use what I gave you)

If you are just determined to be an idiot then no-one can help you (maybe give up on trying to write software).

Or maybe *you* should stop being condescending and at least provide good code.

Your function has the same bug as his and then some.
Code:
#include <sstream>
#include <iomanip>

string print_hash( const string& hash )
{
   ostringstream outs;

   if( hash.length == 32 )
   {
      for( size_t i = 0; i < 32; i++ )
         outs << hex << setw( 2 ) << setfill( '0' ) << ( unsigned )hash[ i ];
   }

   return outs.str( );
}

1. length is a method, not a field. It should be length()
2. you are missing the using clauses. At least 'using namespace std;'
3. not a bug per say but misleading name: 'print_hash' does not print the hash. It converts it to a hex string.
4. the reason why the output have sometimes ff is because of the (unsigned) cast. It casts the char to (unsigned int). Since there is no
direct conversion path, it goes char -> int -> unsigned int. When the char is negative (because >= 0x80), the promotion to int preserves the negative sign. The proper way is to do in two steps
Code:
(int)(unsigned char)hash[i]

Or in the OP's way
Code:
printf("%02x", (unsigned char)myStringSHA[i]);




CIYAM
Legendary
*
Offline Offline

Activity: 1890
Merit: 1086


Ian Knowles - CIYAM Lead Developer


View Profile WWW
March 11, 2015, 06:24:50 AM
Last edit: March 11, 2015, 09:33:55 AM by CIYAM
 #15

Or maybe *you* should stop being condescending and at least provide good code.

I was not meaning to be condescending (but admittedly got a bit frustrated - I have now removed the offensive comments from my post).

Here is the fixed code (that I have tested with g++) that will work assuming that your string is the binary hash:

Code:
#include <iomanip>
#include <sstream>
#include <iostream>

using namespace std;

string print_hash_as_hex( const string& hash )
{
   ostringstream outs;

   if( hash.length( ) == 32 )
   {
      for( size_t i = 0; i < 32; i++ )
         outs << hex << setw( 2 ) << setfill( '0' ) << ( int )( unsigned char )hash[ i ];
   }

   return outs.str( );
}

int main( )
{
   string hash( "\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a\x9a" );

   cout << print_hash_as_hex( hash );
}

Replace the first line in "main" with your SHA256 string object.

With CIYAM anyone can create 100% generated C++ web applications in literally minutes.

GPG Public Key | 1ciyam3htJit1feGa26p2wQ4aw6KFTejU
redPanda (OP)
Member
**
Offline Offline

Activity: 65
Merit: 16


View Profile
March 11, 2015, 02:16:05 PM
Last edit: March 11, 2015, 02:32:13 PM by redPanda
 #16

If I use the two steps conversion path, I can get rid of the trailling 'f'
but I still miss the last characters (the size of my string is 29).
When I use your advice:
Quote
Replace
 return string((char*)abDigest);
With   return string((char*)abDigest, 32);
then I get the perfect result.
Great!
and thanks for your explanations, I like to understand what I'm doing
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!