SHA 256 from scratch with pen and paper

Translations: German 🇩🇪

I like to take things to the extreme in all my hobbies/interests – that’s just me. Previously I have shown how to make a Bitcoin seed phrase from scratch using dice or coins. No computer device is required up to the point of calculating the checksum because that involves hashing the random part of the binary seed.

We can theoretically eliminate the need for a computer beyond this point by learning to make a SHA-256 calculation by hand. For more information on what SHA-256 is (and how it relates to Bitcoin Mining), see here

Please understand, I am not advocating that this is what you should do for your Bitcoin wallets; I just think this is cool. You’re welcome to judge me. Practically, you will always need a computer to make a wallet. Once you have the seed phrase, good luck using cryptography to make your private and public keys by hand!

For background knowledge and understanding, I have some extra information in the appendix. I cover binary numbers, bits, bytes, hexadecimal numbers, converting numbers, and converting text to binary.

The formula for SHA256 is in the public domain. The formula is specific and reproducible, but it’s not easy to understand. I have deciphered it for myself and reproduced it here in a way for a non-computer scientist to understand.

To demonstrate it, I will hash the “Bacon Bitcoin Wallet”. As you might know, the checksum word (24th word) for the seed phrase containing twenty-three times the word, “bacon”, is “bacon”, resulting in a seed that is “bacon”, twenty-four times. (To be technically correct, there are 7 other words that are valid after bacon x 23). This guide will show how that word is calculated to demonstrate how to hash by hand.

Part 1: Pre-Processing

Part 1, Step 1 – get binary for the input.

If you wanted to hash some text, then you’d need to convert it to binary (see appendix). For this guide, I’m hashing a binary number anyway, so no conversion is necessary. I’m going to hash the random binary component of the bacon-24 BIP39 wallet which is the following (excluding spaces):

The group of 11 binary digits repeated above, 00010001010, is the number 138 in the decimal system, which maps to the word “bacon” on the BIP 39 protocol wordlist 

You might notice on the GitHub list, that “bacon” seems to be word number 139, but it’s not – that’s just GitHub autoformatting the lines of text starting at 1 instead of 0, resulting in a shift of all the words to one number higher. This becomes obvious when you look at the first word on the list, “abandon”, which is labelled 1 instead of 0. 

The first 253 digits above (11×23=253) represent bacon 23 times. Notice there are three extra bits at the end that doesn’t make up a word (you need 11 bits to make a word, so 8 bits are missing to make the 24th word). These 8 digits are not part of the random collection of 256 bits, but the first three are. Because I know what the result will be in advance, I know that if the last three random digits were anything other than “000”, the checksum (24th word) will end up being something other than “bacon”. Yes, that wallet would also be valid, but just not as cool. In fact, because “000” could randomly be anything between 000 and 111, there are 8 different combinations possible, and so, 8 different possible checksum words after baconx23.

Part 1, Step 2 – append a ‘1’ to the input.

Part 1, Step 3 – Append 0’s until the total number is 64 digits short of a multiple of 512.

I started with 256 bits, and added one more in step two, so there are 257 bits. The next multiple of 512 above 257 is 512. Subtracting 64, you get 448. So I need to add 191 zeros:

If you were going to hash something else, and the length of your input was between 448 and 512 (less than 64 away from 512), say, 500, then you’d need to go up to the next multiple, 1024, and subtract 64 from that to get your new length, then add zeros to make your input that length –  because we always need to make the number 64 digits less than a multiple of 512. Similarly, if the length of your input were between 960 and 1024, then you’d need to go up to the third multiple of 512 less 64.

Part 1, Step 4 – add the length in binary

The input now has 448 digits. We need to convert 448 to a binary number.

To do that by hand, see the appendix.

The answer is 11100000

EDIT: That’s incorrect, note it’s actually 111000000, discovered after all the work has been done. I will leave the error in place for the workings that follow, as the guide is to demonstrate the calculations, not to actually find the hash – we know what the answer should be anyway. Thanks to @darrenahunter for bringing this to my attention.

Now we add this binary as a 64-digit big-endian binary. A whaty-whaty what? I had to learn about this and look it up. Put very simply, it refers to the direction a computer stores a number (left to right, or right to left). The instructions we have are to use 64 digits to write the binary above and use leading zeros to fill the space. That’d be 56 zeros followed by 11100000, then we append those 64 bits to the end of the input.

Now we have 512 bits.

Part II: Initialise Hash Values

There are 8 constants as part of the SHA256 hashing algorithm, h0 to h7, called “hash values”. 

The 8 hash values “represent the first 32 bits of the fractional parts of the square roots of the first 8 primes: 2, 3, 5, 7, 11, 13, 17, and 19” – again, whaty-whaty what? See the appendix for how to derive them, but they are constants – they’ll be the same for any hash you do.

They are as follows:

h0 = 0x6a09e667

h1 = 0xbb67ae85

h2 = 0x3c6ef372

h3 = 0xa54ff53a

h4 = 0x510e527f

h5 = 0x9b05688c

h6 = 0x1f83d9ab

h7 = 0x5be0cd19

The “0x” prefix indicates that the numbers that follow are encoded in hexadecimal – “0x” is not contributing to the value, it’s just a symbol. Rewritten in binary we have:

h0 = 01101010000010011110011001100111

h1 = 10111011011001111010111010000101

h2 = 00111100011011101111001101110010

h3 = 10100101010011111111010100111010

h4 = 01010001000011100101001001111111

h5 = 10011011000001010110100010001100

h6 = 00011111100000111101100110101011

h7 = 01011011111000001100110100011001

Having labels for “h0” to “h7” is useful, because as we progress through the hashing procedure, the values they contain will be modified.

Part III: Round constants

We have more constants now – 64 of them, called k0 to k63.

These are the cube roots of the first 64 primes, and just like the hash values, for each, we take the decimal part and convert it to an eight-digit hex (or 32-bit binary).

Again, these are constants and apply to any hash you do.

I have demonstrated how to work out the hash values in the appendix, the principles apply to the round constants too, so I haven’t repeated them. The values are:

0x428a2f98 0x71374491 0xb5c0fbcf 0xe9b5dba5 

0x3956c25b 0x59f111f1 0x923f82a4 0xab1c5ed5

0xd807aa98 0x12835b01 0x243185be 0x550c7dc3 

0x72be5d74 0x80deb1fe 0x9bdc06a7 0xc19bf174

0xe49b69c1 0xefbe4786 0x0fc19dc6 0x240ca1cc 

0x2de92c6f 0x4a7484aa 0x5cb0a9dc 0x76f988da

0x983e5152 0xa831c66d 0xb00327c8 0xbf597fc7 

0xc6e00bf3 0xd5a79147 0x06ca6351 0x14292967

0x27b70a85 0x2e1b2138 0x4d2c6dfc 0x53380d13 

0x650a7354 0x766a0abb 0x81c2c92e 0x92722c85

0xa2bfe8a1 0xa81a664b 0xc24b8b70 0xc76c51a3 

0xd192e819 0xd6990624 0xf40e3585 0x106aa070

0x19a4c116 0x1e376c08 0x2748774c 0x34b0bcb5 

0x391c0cb3 0x4ed8aa4a 0x5b9cca4f 0x682e6ff3

0x748f82ee 0x78a5636f 0x84c87814 0x8cc70208 

0x90befffa 0xa4506ceb 0xbef9a3f7 0xc67178f2

Part 4: Chunk loop

What have we got so far?

  • A modified input of 512 bits (but we could have had higher multiples of 512 if our input was larger).
  • 8 hash values.
  • 64 round constants.

Each 512-bit group is called a “chunk”. 

We must perform some operations with the chunk data against the 8 hash values which will modify them. If there is only one chunk, those 8 values are stringed together at the end to make the final hash. If there are more chunks, the 8 hash values become the starting point of the next chunk, and it progresses through them all.

The loop begins by expanding the allocated 512 bits we have to 2048 bits (by adding zeros),  and each 32 bit group of the 2048 bit whole is then labelled w0 to w63:

What do we have now?

  • 32 “words”: w0 to w15 (belonging to chunk 1), and new zeros from w16 to w63, modifying the chunk into 2048 bits total.
  • 8 hash values
  • 64 round constants

Rewriting the chunk bits grouped into “words” w0 to w63:

I have used some abbreviations to not unnecessarily write out long lines of zeros.

The new words (w16 to w63) are then taken through an algorithm of modifications. The other words (w0 to w15) make an appearance when called on, as will be seen.

I will write out the instructions, and then perform the execution step by step which will explain what the code means.

The chunk “instructions”:

Create variables S0, S1

Then, for i in w[16-63]: (in programmers’ speak, this means to let i sequentially equal the values 16 to 63, and go through the instructions that follow in a loop, using i as the number 16 through 63). 

The instructions might be confusing at first glance, but as I take you through an example, it will become easy to understand:

Instructions:

  • S0=(w[i-15] rightrotate 7) XOR (w[i-15] rightrotate 18) XOR (w[i-15] rightSHIFT 3)
  • S1=(w[i-2] rightrotate 17) XOR (w[i-2] rightrotate 19) XOR (w[i-2] rightSHIFT 10)

After calculating S1 and S2, use them to create w[i]:

  • w[i]=w[i-16] + S0 + w[i-7] + S1 (addition with modulus 2^32)

Before carrying this code out with pen and paper, I’ll explain some things:

w[i-15] in equation 1 means we’ll substitute i’s value, at first, it’s 16, but we repeat for every value of i from 16 to 63. The first time we’ll get w[1], meaning we look up word 1 (NOTE THIS IS THE SECOND WORD ON THE LIST, WORD 0 IS THE FIRST) and substitute the binary numbers of that word into the equation.

The next thing to discuss is “rightrotate”. This means we take a binary number, then take its digit on the far right and place it on the far left of the number. Repeat this for the number of times indicated after the word “rightrotate”.

Now consider “rightshift”. This means removing the digit on the far right and adding a zero to the far left. Repeat this for the number of times indicated.

The XOR operation means to take the binary number on the left (call it A) and compare it to the binary on the right (call it B), digit by digit, starting from the far right and moving left. For each digit place, make a result (output) based on the following table:

Putting it in words –  “OR” refers to one or the other being positive, but not both. So row two and three in the table have positive results. One of A or B is a “1”, satisfying “XOR”, so the result is “1”. In the first row, both A and B are negative, so we get “zero” as the output. 

The final row is a bit odd, and not intuitive. How “XOR” works here is different to what you might expect compared to how programming languages handle the “OR” operation. It might look like it satisfies a logical “OR”, but because both are positive, the output is false, or “0”.

Finally, we need to know how to add binaries with modulus 2^32. This basically means: add the binary numbers but as the answer grows (in the number of digits), if a 33rd digit is created, discard it. 

The method for adding: adding two 0’s gives a 0. Adding a 0 and a 1 gives a 1. Adding two 1’s gives a zero for that position, but we carry a 1 to the next position (one to the left).

For example, for 11001 + 11111 (modulus 2^5), we add the two 1’s at the far right, and get a 0, and carry a 1. For the second position we have zero, a 1, and a carried 1, so the answer is zero, and carry a 1. For the third position it’s a 0, a 1, and carried 1, so again the answer is 0 and carry a 1. For the 4th position, we have three 1’s so the answer is 1 and a carried 1. For the 5th position, we have three 1’s again, so the answer is 1 and a carried 1. Because the carried 1 is to the 6th position, and the modulus is 2^5, we simply drop it from the answer. The final result is:

11000

The point of the modulus in this example is to keep the answer the same length as the inputs (5 bits). For the actual hashing formula, the words are 32 bits long, so using 2^32 modulus keeps the answer to 32 bits and no greater.

Executing the chunk code by hand:

The formula for S0 is written out for reference. In the next line, I have copied out the second “word” (word[1]), which is the second group of 32 bits of the 512-bit “chunk”. We can see it starts with a zero (in black), then the word “bacon” in binary (red), then “bacon” again (blue), then the green digits at the end are the first 9 digits of “bacon”.

Next, I have taken the far right 7 digits of w1 and placed them at the beginning, rewriting word 1 on the next line (called “w1(RR7)”). The rightrotate (7) operation has now been performed on word 1.

Next, I have taken word 1 from the original and rightrotate’d it this time with 18 digits, then I wrote it out in that modified form on the final line.

Next, I will perform an XOR operation with the two inputs being the two new temporary versions of word 1 (RR7 and RR18). It’s easier on the eye if you draw some lines between two digits that are being analysed.

I have performed the first XOR operation, but the S0 equation has a second. 

The next part of the S0 equation requires a rightshift(3) of the w1 word (labelled “W1(RS3)”).

To do that, I dropped the far right three digits and added three 0’s to the beginning of word 1. Then, I performed the second XOR function (XOR2). The first input is the result of XOR1, and the second input is W1(RS3). 

The result of XOR2 is equal to S0 when i=16; now I’ll do S1.

I have so far just simply rewritten the formula for S1, and the word [i-2] which is word 14, in green. Note I call it “word 14”, not the “14th word” because it’s actually the 15th word (the list starts at zero). 

Of note, be careful as you go to higher values of i, as most of the words in this example are zero, but word 15 isn’t.

You can actually do the equation for S1 in your head. Want to try before reading on?

Answer: all zeros.

Why? Rightshifting or rightrotating an all-zero word results in all zeros. So the numbers in the three brackets all equal zero. Then, performing XOR operations on zeros results in zero. So the first and second XOR operations return 32 zeros…

We’re almost at the end of part 4.

Next, going back to this equation:

  • w[i]=w[i-16] + S0 + w[i-7] + S1 (addition with modulus 2^32)

When i is 16, we are looking for a new w16, using w0, S0, w9, and S1. We just add them all together one by one to get w16, which will change from its current state of all zeros, to something else.

In red we have a work in progress. Next, I’ll add w9, which is all zeros, so the number in red doesn’t change. Next, I’ll add S1, again which is all zeros, so the number in red is final; that’s w[16].

Next, I did i=17, and repeated all of this again. Then i=18, all the way to i=63. I won’t show you the working out to that, as it doesn’t add any further explanation, but let me tell you, it’s exhausting, and I chipped away at it over a couple of weeks.

The following are the new versions of w[16 to 63] after carrying out the chunk instructions:

w[16] = 00010011010111011010110101001101

w[17] = 00011001110111011110010101100100

w[18] = 00001101110100111101001010110010

w[19] = 01010111111010011100100101011000

w[20] = 10101011111101001110010010101100

w[21] = 01010111111000010001000101000010

w[22] = 00101011111111010001101000001011

w[23] = 00100101001000101010010001010000

w]24] = 10000000000000000000000000000000

w[25] = 00000000000000000000000000000000

w[26] = 00000000000000000000000000000000

w[27] = 00000000000000000000000000000000

w[28] = 00000000000000000000000000000000

w[29] = 00000000000000000000000000000000

w[30] = 11000000001110000000000000011101

w[31] = 00000000000000000000000011100000

W[32-63] = 00000000000000000000000000000000

Note that this has to be done for each 512-bit chunk. That is, take each 512-bit chunk from the pre-processing stage, expand each to 2048 bits (64 x 32-bit “words”), then modify words 16 to 63 (17th to 64th). If you started with 2 chunks (2 x 512 bits), you would have 128 “words” at this stage.

Part 5: Compression loop

In this part, things are really going to get spicy. The modified input (w[0] to w[64]) for each chunk will blend with the k constants and the initial h values, through a series of instructions, and at the end of each chunk’s manipulation, the values h[0] to h[8] will be modified and passed on as the next chunk’s initial h values. In the end, these h values concatenated (joined together) make up the final hash.

The instructions:

Set the values of a to h equal to h0 to h7.

All additions are calculated modulo 2^32.

Perform a loop: for i in (0 to 63):

S1 = (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)

ch = (e and f) xor ((not e) and g)

temp1 = h + S1 + ch + k[i] + w[i]

S0 = (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)

maj = (a and b) xor (a and c) xor (b and c)

temp2 = S0 + maj

h = g

g = f

f = e

e = d + temp1

d = c

c = b

b = a

a = temp1 + temp2

Executing the code by hand:

I start by letting i=0

To work out S1…

S1 = (e rightrotate 6) xor (e rightrotate 11) xor (e rightrotate 25)

I have rightrotate’d “e” (which is a copy of h4) by 6, and then XOR’d it with a rightrotation 11 of “e”. I called that “XOR1” below. Then I XOR’d that with a rightrotation 25 of “e”, resulting in “XOR2”, which is equal to “S1”.

Next, I work out the value for “ch”

ch = (e and f) xor ((not e) and g)

Both e and f were copied from h4 and h5 respectively. I already have e, so I’ve written down f and AND’d it in the line below. 

Then I wrote down the value for NOT(e). That’s basically the opposite of e. That means, where there is a 0, replace it with a 1, and vice versa. Then I wrote down g and AND’d it with NOT(e) in the line below.

Finally, that line was XOR’d with the (e AND f) result to give “ch”

Next, I work out the value of “temp1”:

temp1 = h + S1 + ch + k[i] + w[i]

I’ve written down the value of h first.

Then added it with S1 (Note, adding is not the same as AND).

Then I’ve added ch to the line above.

Then written down the value of k[0]. The k is one of the “round constants” from Part III. I used zeroth one because i=0. Later i will equal all the values up to 63, so this is a big loop.

Next, I work out “temp1” by adding w[0] with the line above it.

Next, I need to work out the value of S0:

S0 = (a rightrotate 2) xor (a rightrotate 13) xor (a rightrotate 22)

Next, I need to calculate a new variable, “maj”:

maj = (a and b) xor (a and c) xor (b and c)

Next, I need to calculate temp2

temp2 = S0 + maj

Now the letters a to h need to be reassigned.

‘h’ becomes ‘g’, ‘g’ becomes ‘f’, ‘f’ becomes ‘e’:

‘e’ becomes ‘d’ plus ‘temp1’:

‘d’ becomes ‘c’, ‘c’ becomes ‘b’, and ‘b’ becomes ‘a’:

d = c

c = b

b = a

Finally, ‘a’ becomes ‘temp1’ plus ‘temp2’:

We now have new values for ‘a’ to ‘h’

That was one loop for i=1. Now, I need to do it all again, SIXTY-THREE MORE TIMES, and with each loop, the letters ‘a’ to ‘h’ get modified.

You know what? I’m not going to do it. This is ridiculous! Wait, I can’t let this go, my personality doesn’t allow me to drop it. I will abbreviate here, but I will carry on and finish this hash properly one day, by hand, and report back with an update.

Part 6: The final hash

In the end, there will be values for ‘a’ to ‘h’ after the 64th iteration. These values “concatenated”, or appended together, make up the hash. You could represent it as one long binary, 256 bits long, or you could convert each 32-bit letter to an 8-digit hex value, and you’ll have a 64-digit hash, which is the usual representation of SHA-256. For this binary, the 8 hash values are:

h1 =8a389dba

h2 = bbeda4b1

h3 = ba4ee074

h4 =01e38740

h5 = 4e4ac579

h6 = 98f195d0

h7 = d22f547c

h8 = 00af49aa

Concatenating them together, we have:

8a389dbabbeda4b1ba4ee07401e387404e4ac57998f195d0d22f547c00af49aa

The first two hex digits in binary are:

1000 1010

Adding them to the last 3 digits of the input (“000”), we get 00010001010, which translates in BIP39 to the word “bacon”. Voilà!

OK, now here’s the shortcut! Put this command (all in one line and press <enter> at the end) in the Linux or mac terminal:

echo 0001000101000010001010000100010100001000101000010001010000100010100001000101000010001010000100010100001000101000010001010000100010100001000101000010001010000100010100001000101000010001010000100010100001000101000010001010000100010100001000101000010001010000 | shasum -a 256 -0

And you’ll get the same hash.

Conclusion

I hope you enjoyed this madness. I learned a lot myself and found the logical operations on binary interesting. I hope I can overcome the urge to complete all 63 more iterations of Part 5, but I doubt I’ll win that battle against my mind.

Appendix A

What is binary?

Binary is a numbering system with only two available symbols, 0 and 1. Imagine counting sheep upward from 0 with only these symbols. You start with 0, then 1, and then to represent another sheep, what do you do? There is no “2” symbol, you have to make do with “0”s and “1”s. The solution is to add a second symbol like this: “10”. 

So, after “1” comes “10”, and after “10” comes “11”, and after that, “100”. Then “101”, “110”, “111”, running out of symbols again, we add another, “1000”, etc.

What is a bit?

A bit is the smallest unit of information for a computer, represented as a “1” or “0”. Translating to the physical computer that holds the data, and depending on the storage medium: positive magnetic field vs negative magnetic field. In logical systems, it represents “true” vs “false”. In machines or circuits, “on” vs “off”.

When related to private keys, a bit can be a recording of a random binary event such as a coin toss, a dice roll (odd vs even; or 1,2,3 vs 4,5,6)

To record the number 11001010 for example, a computer can print magnetic fields side by side as: 

positive : positive : negative : negative : positive : negative : positive : negative

What is a byte?

A byte is 8 bits. It was to be the amount of data allocated on a computer to record a single text character (The original protocol was ASCII, but there are many more, and Unicode is preferred now, generally). Given that a byte can represent a number anywhere between 00000000 minimum to 11111111 maximum (converted to decimal, that’s 0 to 255), 256 possible characters could be represented with a byte using the ASCII protocol.

What is hexadecimal (hex)?

Hexadecimal is another numbering system. Instead of 10 symbols (decimal) or two symbols (binary), there are 16 (hex = 6, decimal = 10, hexadecimal = 16). 

There are the usual decimal symbols “0” to “9”, but also “a” to “f”, representing the values 10 to 15. For example, “f” in hex is “15” in decimal. And “10” in hexadecimal is actually “16” in decimal.

Do you know what “20” in hexadecimal would be in decimal? The “2” in the second position means we’ve counted up to 16 (decimal) twice. We got to “f” (value 15), then kept going to the value 16 (“10” in hex), and then counted further to 11 (in hex). Look at this number in hex: 11 – do you understand it’s not “11” as you know it? It’s bigger. Think how we got there. It actually represents the value 17. 

To keep counting up in hex after 11, we write 12, 13, 14, 15, 16, 17, 18, 19, and then 1a. What is this? The 1 in the second position represents “16” in decimal, and the “a” has a value of 10. So, 1a is actually 26 in decimal. Keep counting up, 1b, 1c, 1d, 1e, 1f. Here we have 16 (decimal) plus 15 (decimal) which is a total of 31 in decimal. Can you work out what adding 1 more to 1f would do without looking at the next paragraph?

The answer is 20. Not a normal 20, but a hex 20. It means we counted to 16 (dec) twice.

Don’t worry if you don’t get it straight away (I certainly didn’t), it’s not intuitive and very abstract. Come back to it later. Also, it’s not crucial to fully understand to continue on with the guide.

When writing a number and indicating it is in hexadecimal, the custom is to precede the number with 0x. For example 16 in decimal, is 10 in hex. We write: 

0x10

How to convert a decimal number to hexadecimal.

If you’re going to be a purist like me and avoid any electronic device, you need to know how to convert a decimal number to hexadecimal for this project:

Eg, let’s convert 6.15 decimal, to hex:

The first digit is a 6 in decimal, and in hex. Write that down in the answer to begin with:

Running answer: 0x6

Next, subtract the 6 from the decimal, we have 0.15 left. Multiply that by 16. We get 2.4. Take the 2, converted it to hex (2), and add it to the hex answer, with a decimal point:

Running answer: 0x6.2

Next, subtract the 2 from the decimal 2.4, we get 0.4. Multiply by 16, and we get 6.4. Put a 6 in the hex result:

Running answer: 0x6.26

Next, remove the 6 from the 6.4, and we get 0.4 (again). Because we’re always going to get 0.4 at this step from now on, the answer is:

Answer: 0x6.26666666666

For larger numbers, let’s take a meaningless number, oh, I don’t know, 21,000,000 and convert it to hex. We first divide by 16:

1,312,500. There is no remainder, so the first digit of the answer (from right to left) is “0”. Continue by dividing 1,312,500 by 16:

82031 and 4/16. Next, we write “4” in the answer in the second from the right position.

Continue by dividing 82031 by 16:

5126 and 15/16.

The remainder is 15, or “f”. Put “f” in the 3rd from the right position in the answer. Continuing on in the same way, until we get the final answer:

Answer: 0x1406F40

The above number is how to write 21,000,000 in hex.

Converting text to binary

If what you are hashing is text, then you must convert the text to a binary number first. For example, the words, “Craig Wright is a liar and a fraud” can be converted to binary using an ASCII to binary conversion table (the conversion is a protocol, not mathematics):

01000011 01110010 01100001 01101001 01100111 00100000 01010111 01110010 01101001 01100111 01101000 01110100 00100000 01101001 01110011 00100000 01100001 00100000 01101100 01101001 01100001 01110010 00100000 01100001 01101110 01100100 00100000 01100001 00100000 01100110 01110010 01100001 01110101 01100100

Above, using the ASCII protocol, each character in “Craig Wright is a liar and a fraud” including spaces has its own binary number made of 1 byte (8 bits).

We could use a different protocol (there are many). Another is Unicode-16. In that case, the binary would be:

01000011 00000000 01110010 00000000 01100001 00000000 01101001 00000000 01100111 00000000 00100000 00000000 01010111 00000000 01110010 00000000 01101001 00000000 01100111 00000000 01101000 00000000 01110100 00000000 00100000 00000000 01101001 00000000 01110011 00000000 00100000 00000000 01100001 00000000 00100000 00000000 01101100 00000000 01101001 00000000 01100001 00000000 01110010 00000000 00100000 00000000 01100001 00000000 01101110 00000000 01100100 00000000 00100000 00000000 01100001 00000000 00100000 00000000 01100110 00000000 01110010 00000000 01100001 00000000 01110101 00000000 01100100 00000000

Converting decimal to binary

If we have a decimal number, let’s use 448, and wish to convert to binary, we repeatedly divide by 2, and use the remainder as our results. This is the process:

Divide 448 by 2. We get 224 with zero remainder. Zero is our first binary digit.

Running answer: 0

Next, we divide 224 by 2. We get 112 with zero remainder

Running answer: 00

Next, we get 56, with zero remainder

Running answer: 000

Next, 28, zero remainder

Running answer: 0000

Next 14, zero remainder:

Running answer: 00000

Next, 7, zero remainder:

Running answer: 00000

Next 3, with 1 remainder:

Running answer: 100000

Next, 1 with 1 remainder (ie 1.5)

Running answer: 1100000

Next 0 with 1 remainder (ie 0.5):

Answer: 11100000

So 448 in decimal is 11100000 in binary.

Converting Hexadecimal to binary

This conversion is actually easy. Each hex digit maps to a 4-digit binary. The largest hex symbol is f, which is 1111 in binary. The smallest hex symbol is 0, which is 0000 in binary. So, the number 1a4f3 in hex is:

0001 1010 0100 1111 0011

And the number 11 1111 in binary is first re-written as 0011 1111 and converts to 3f

Deriving the 8 hash values

The 8 values start with the first 8 prime numbers, respectively. I’ll show the first one only, derived from the prime number 2.

We first take the square root: 

1.41421356237

Now, we just want the fractional part (numbers to the right of the decimal point) in hex. It’s not obvious how to do that. You can’t simply multiply by factors of 10 to move the decimal point to the right. Why? Because it’s not a decimal point, it’s a “hexadecimal point” – you have to multiply by 16! Here’s how:

Remove the 1, since we only want the fractional component:

0.41421356237

Multiply that by 16:

6.6274

Take the 6 and convert it to hex, which is 6, and add to the running answer:

Running answer: 6

Now remove the 6, we get 0.6274, and multiply by 16. We get 10.038. Take the “10” and convert it to hex, we get “a”. Add that to the running answer:

Running answer: 6a

Then we remove the 10, and carry on withe the 0.038. Keep cycling until you get 8 hex digits in the answer. This converts to 32 binary bits.

Deriving the Initial Hash Values

The 8 hash values represent the first 32 bits of the fractional parts of the square roots of the first 8 primes: 2, 3, 5, 7, 11, 13, 17, and 19.

I will demonstrate using the first prime, number 2.

First, take the square root of 2:

1.41421356237

Then take only the “fractional” part. (ie remove the integer)

0.41421356237

If we just needed the fractional part in decimal, we’d simply multiply by 10 over and over until we get:

41421356237

But we want “the first 32 bits”, meaning 32 1’s and 0’s. How do you do that from a decimal number? The simpler way is to get 8 hexadecimal numbers, then convert that to 32 bits of binary (each hex number is 4 bits of binary, a simple conversion which I will show.)

So instead of multiplying the above number by 10, we multiply by 16, and extract the integer.

0.41421356237 x 16 = 6.62741699797

6 is the number we need, which is ‘6’ in hexadecimal. That’s our first digit for the hash value. Now subtract 6 from the answer above, and multiply by 16:

0.62741699797 x 16 = 10.0386719675

10 is the number we need, which is ‘a’ in hexadecimal. That’s our first digit for the hash value. Then we subtract 10 from the answer above, and multiply by 16. Keep repeating until you get 8 digits of hexadecimal, then stop. We get:

6a09e667 in hex, also written as 0x6a09e667 where “0x” indicates that the following number is in hex.

This number is easily converted to binary. Each digit is between binary 0000-1111 (ie decimal 0 to 16, or hexadecimal 0 to f).

HexadecimalBinary
60110
a1010
00000
91001
e1110
60110
60110
70111

Putting all the binary numbers together in order, we have h1 as:

01101010000010011110011001100111 in binary

Appendix B – Proof of my work for Chunk loop 1 

i=17

i=18

Appendix C – Proof of Work for the remainder of the entire hash (to be continued)

Links

https://sha256algorithm.com/


On-chain or Lightning