How to calculate SHA256 of a number, not string

Observation

It seems that Mathematica's Hash interprets the input as a number.

For instance:

In[1]:= IntegerString[Hash[0, "SHA256"], 16, 64]

Out[1]= "3d2657e44444e31b63ca19355e31677556fdc59d5e9e9382140fa9953816a7af"

In[2]:= IntegerString[Hash["0", "SHA256"], 16, 64]

Out[2]= "5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9"

If we compare these hashes with the output of the string hash function of the online hash calculator:

5feceb66ffc86f38d952786c6d696c79c2dbc239dd4e91b46729d73a27fb57e9

However, if we put 0 in the binary hash, we get:

6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d

Why the difference then?

I believe the difference occurs because Mathematica likes to store values using 2^n bytes.

For instance, your number of interest is 42/2 = 21 bytes:

In[3]:= StringLength["00010966776006953D5567439E5E39F86A0D273BEE"]

Out[3]= 42

But, Mathematica stores it in 64 bytes:

In[4]:= FromDigits["00010966776006953D5567439E5E39F86A0D273BEE", 16]

Out[4]= 5918623552017386584225127869298065229098728430

In[5]:= ByteCount@5918623552017386584225127869298065229098728430

Out[5]= 64

This causes the difference.

How do I get the correct hash?

Taking @george2079 's advice, you could use BinaryWrite and FileHash (unfortunately, Hashing a ByteArray includes the head ByteArray itself, so you don't get the correct hash).

That is:

In[6]:= temp = $TemporaryDirectory <> "temp";
 BinaryWrite[temp, 
  FromDigits[#, 16] & /@ 
   StringPartition["00010966776006953D5567439E5E39F86A0D273BEE", 2]];
 Close[temp];
 IntegerString[FileHash[temp, "SHA256"], 16, 64]

Out[6]:= "445c7a8007a93d8733188288bb320a8fe2debd2ae1b47f0f50bc10bae845c094"

If you have Mathematica version prior to 10.1, instead of StringPartition, use:

StringJoin @@@ Partition[
  Characters["00010966776006953D5567439E5E39F86A0D273BEE"], 2]

I found the issue. In the end of my question, I proposed converting the number to a list of bytes and then to a string from these bytes using:

FromCharacterCode@
   IntegerDigits[
    FromDigits["00010966776006953D5567439E5E39F86A0D273BEE", 16], 
    256]

But my number had trailing zeros on the left, so the conversion to integer created a number which was not 21 bytes and the resulting string was wrong. It also explains why it sometimes worked. This is the correct way to do the conversion of hexadecimal number to a string suitable for hashing in Mathematica:

sha256[string_] := IntegerString[
  Hash[
   FromCharacterCode[
    FromDigits[#, 16] & /@ StringPartition[string, 2]
    ], "SHA256"
   ], 16, 64]

where string_ is a hexadecimal number. It finally gives the correct answer to my problem and it is more than three times faster than the BinaryWrite solution.

In[1]:= sha256["00010966776006953D5567439E5E39F86A0D273BEE"]

Out[1]= "445c7a8007a93d8733188288bb320a8fe2debd2ae1b47f0f50bc10bae845c094"

Hash has been updated in version 11.3.0 and offers a somewhat easier way to do this

hex = "00010966776006953D5567439E5E39F86A0D273BEE";

bytes = IntegerDigits[FromDigits[hex, 16], 256, StringLength[hex]/2];

Hash[ByteArray[bytes], "SHA256", "HexString"]

(* "445c7a8007a93d8733188288bb320a8fe2debd2ae1b47f0f50bc10bae845c094" *)

Tags:

Hashing