How do you perform double-SHA-256 encoding?

17

3

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.

nyusternie

Posted 2012-12-08T22:53:51.103

Reputation: 575

3Just 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 2016-02-29T18:48:51.723

Answers

25

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

theymos

Posted 2012-12-08T22:53:51.103

Reputation: 8 228

1This seems right, but how did the first round work when hashing the ASCII string "HELLO" vs the binary representation of ASCII hello?goodguys_activate 2012-12-09T02:44:56.153

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 2012-12-09T04:08:05.817

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 2012-12-09T06:14:00.783

theymos and @DavidSchwartz it all makes perfect sense now. thanksnyusternie 2012-12-11T21:11:07.930

Hi, there are an optimized (direct and faster) option to perform SHA256d? Some openssl or similar (reliable) optimized algorith for it?Peter Krauss 2018-05-08T09:27:30.443

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'

Colin Dean

Posted 2012-12-08T22:53:51.103

Reputation: 6 559

thanks for code samples. I later found the Python code on the wiki, but still couldn't understand what made it work. +1nyusternie 2012-12-11T21:07:16.473

3

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.

RocketNuts

Posted 2012-12-08T22:53:51.103

Reputation: 714

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).

mulllhausen

Posted 2012-12-08T22:53:51.103

Reputation: 1 533