21

The Bitcoin Protocol-specification gives an example of double-SHA-256 encoding.

hello
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 (first round of sha-256)
9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50 (second round of sha-256)

I've tried various SHA256 calculators and the first encoding matches no problem, but the second always resolves to

d7914fe546b684688bb95f4f888a92dfc680603a75f23eb823658031fff766d9

I've also tried UPPERCASE and swapping the byte endianness.

1
  • 4
    Just semantics, but to avoid a common misunderstanding: sha256 does hashing, not encoding. Encoding is something entirely different. For one it implies it can be decoded, whereas hashing is strictly a one-way (and destructive) operation. – RocketNuts Feb 29 '16 at 18:48
31

You're hashing the hexadecimal representation of the first hash. You need to hash the actual hash -- the binary data that the hex represents.

Try this:

$ echo -n hello |openssl dgst -sha256 -binary |openssl dgst -sha256
5
  • 1
    This seems right, but how did the first round work when hashing the ASCII string "HELLO" vs the binary representation of ASCII hello? – halfbit Dec 9 '12 at 2:44
  • 2
    @makerofthings7 "HELLO" is binary. H is a byte with value 72, E is 69, etc. A raw, binary hash can also be printed, but many of its characters are likely to be invisible control characters, so it's generally written in hex. I can also write "HELLO" in hex: it's 48454c4c4f. – theymos Dec 9 '12 at 4:08
  • 2
    @makerofthings7: There is no difference between "the ASCII string 'HELLO' and "the binary representation of ASCII 'HELLO'". However, there is a difference between the ASCII representation of a number in hexadecimal and that number in binary. – David Schwartz Dec 9 '12 at 6:14
  • theymos and @DavidSchwartz it all makes perfect sense now. thanks – nyusternie Dec 11 '12 at 21:11
  • Hi, there are an optimized (direct and faster) option to perform SHA256d? Some openssl or similar (reliable) optimized algorith for it? – Peter Krauss May 8 '18 at 9:27
8

You want to work with the digests, not the hex strings.

Here's some Ruby:

require 'digest'
d = Digest::SHA2.new 256
d2 = Digest::SHA2.new 256
d << 'hello'
d.to_s
d2 << d.digest
d2.to_s

This will be the output from irb:

1.9.3p194 :001 > require 'digest'
 => true 
1.9.3p194 :003 >   d = Digest::SHA2.new 256
 => #<Digest::SHA2:256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855> 
1.9.3p194 :004 > d2 = Digest::SHA2.new 256
 => #<Digest::SHA2:256 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855> 
1.9.3p194 :005 > d << 'hello'
 => #<Digest::SHA2:256 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824> 
1.9.3p194 :006 > d.to_s
 => "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" 
1.9.3p194 :007 > d2 << d.digest
 => #<Digest::SHA2:256 9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50> 
1.9.3p194 :008 > d2.to_s
 => "9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50"

Here's the same thing in Python:

import hashlib
d = hashlib.sha256(b"hello")
d2 = hashlib.sha256()
d.hexdigest()
d2.update(d.digest())
d2.hexdigest()

And the output from within a Python shell:

>>> d = hashlib.sha256(b"hello")
>>> d2 = hashlib.sha256()
>>> d.hexdigest()
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824'
>>> d2.update(d.digest())
>>> d2.hexdigest()
'9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50'
1
  • thanks for code samples. I later found the Python code on the wiki, but still couldn't understand what made it work. +1 – nyusternie Dec 11 '12 at 21:07
4

For the second round of sha256, you need to hash the raw binary output from the first round, not the textual version.

A sha256 hash is 256 bits, or 32 bytes. Thus for the second round you should be hashing a piece of data that's 32 bytes. When hashing a hexadecimal string as the literal input for the second round, your data is 64 bytes.

Try a hashing tool that can interpret hexadecimal input. For example see here, copy/paste the hash to the input field and check the 'Hex' checkbox.

0

For easy reference, lets name the various inputs and outputs from your question like so:

pre-image 1:                     hello
                                   |
                                   v
hash operation 1:                SHA256
                                   |
                                   v
hash output 1 (aka pre-image 2): 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
                                   |
                                   v
hash operation 2:                SHA256
                                   |
                                   v
hash output 2:                   9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50

Pre-image 1 is binary (ASCII) data. We know this because the range of hexadecimal characters is 0-9 and a-f, and there are characters beyond f in pre-image 1. So hash operation 1 must be performed using a binary input.

By definition, a SHA256 hash operation always produces a 256 bit integer. We typically represent this in hexadecimal format, however it could just as correctly be written in decimal format and nothing would change:

hex:     2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
decimal: 20329878786436204988385760252021328656300425018755239228739303522659023427620

(Decimal conversion using Wolfram Alpha)

Since we know that pre-image 2 is a hexadecimal number, then hash operation 2 must be performed upon a number, rather than upon binary data.

In Javascript, using the Stanford Javascript Crypto Library, the two hashes would be done like so:

var preImage1 = 'hello';
var hashOutput1 = sjcl.hash.sha256.hash(preImage1);
console.log('hash output 1: ' + sjcl.codec.hex.fromBits(hashOutput1));
var preImage2 = hashOutput1;
var hashOutput2 = sjcl.hash.sha256.hash(preImage2);
console.log('hash output 2: ' + sjcl.codec.hex.fromBits(hashOutput2));

The SJCL is clever enough to figure out the pre-image formats and perform the hash correctly in each case.

You can also do these hash operations live on my blog, here: https://analysis.null.place/how-do-the-bitcoin-mining-algorithms-work/#form10

Note how the hash output changes when you click the pre-image is hexadecimal checkbox (this only works when hexadecimal characters are present in the input).

0

Late to the party, but here is a Node.js implementation using the built-in crypto module:

const crypto = require('crypto');
/**
 *
 * @param {Buffer} data
 * @returns {Buffer}
 */
function doubleSHA256(data) {
  return crypto.createHash('sha256').update(crypto.createHash('sha256').update(data).digest()).digest();
}
-1

It looks like you have coded your algo to always assume that your input msg always contains data in ASCII display format. That would explain why your first algo produced the correct hash table. For run2, you can have your input msg data in any format you like, as long as you have some way of telling your algo what to expect. I am going to assume that when you wrote your algo, that you coded it to always expect ASCII display format in the input msg file. So, if you didn't switch your first output block table to ASCII display format, then that would explain why you didn't get the expected final block table from your second run. Grab a list of "The IBM PC Character Set" and you'll see what I mean. eg if you're providing an 'a' and it's really a hexadecimal 'a' as opposed to a ASCII display format 'a', then you've GOT to let your algo know what you are doing - it can't second guess that you've switched formats without telling it. So similarly, you may have put the hexadecimal hash or even the binary hash table, from run1 output into run2 input, which is fine, BUT you have let your algo know that it should read it as what ever format you picked, to wack into the run2 input file. There are 2 ways to fix this IMO. (1) convert your output hash block table from run1, to ASCII display chars and wack them into the run2 input file. I wouldn't do this because you get some wierd and wonderful ASCII display chars that may do wierd things. (2) Re-code your algo, so that you can tell it what data will be coming at it, from the run2 input file. That's a more flexible way to do things, and it opens up a lot more options in terms of using any base you like, even your own custom ones, but YMMV. A lot of water has passed under the bridge since your problem was published, but this may help others.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.