Published Date : 2021年8月8日4:32

013 Pythonでビットコインを学ぶ (シャー256 3)
013 Use python to learn bitcoin (sha256 3)


This blog has an English translation


ニコニコ動画にアップした動画のまとめ記事です。

This is a summary blog post about a video I uploaded to NicoNico.

細かい部分は動画を参考にしてください。

Please refer to the video for details.


目次

Table of Contents




① 動画の説明
① Video Description



前回の続きです。

Continued from last time.

次に残りの64ビットに元のメッセージの長さのビット数を加えます。

It then adds the number of bits in the original message length to the remaining 64 bits.

count = 0

for bits in converted_str:
    count += len(bits)
    print(f"{bits} -> {len(bits)} bits")

01101000 -> 8 bits
01100101 -> 8 bits
01101100 -> 8 bits
01101100 -> 8 bits
01101111 -> 8 bits
00100000 -> 8 bits
01110111 -> 8 bits
01101111 -> 8 bits
01110010 -> 8 bits
01101100 -> 8 bits
01100100 -> 8 bits

print(count)
88

len(converted_str)
11

len(converted_str) * 8
88

88を8ビットのバイナリ文字列に変換すると[01011000]となります。

Converting 88 to an 8-bit binary string is [01011000].

format(len(converted_str) * 8, '08b')
'01011000'

この値を64ビットの長さになるように調節してメッセージブロックに加えていきます。

Adjust this value to a length of 64 bits and add it to the message block.

format(len(converted_str) * 8, '064b')
'0000000000000000000000000000000000000000000000000000000001011000'

8ビットが8つで64ビットですので、8つの配列になるように調整していきましょう。

Since it is 64 bits, so let's create an array that 8-bit binary strings becomes eight.

format(len(converted_str) * 8, '064b')[0:8]
'00000000'

format(len(converted_str) * 8, '064b')[8:16]
'00000000'

format(len(converted_str) * 8, '064b')[16:24]
'00000000'

format(len(converted_str) * 8, '064b')[24:32]
'00000000'

format(len(converted_str) * 8, '064b')[32:40]
'00000000'

format(len(converted_str) * 8, '064b')[40:48]
'00000000'

format(len(converted_str) * 8, '064b')[48:56]
'00000000'

format(len(converted_str) * 8, '064b')[56:64]
'01011000'

len(format(len(converted_str) * 8, '064b')) // 8
8

len_bitstr = format(len(converted_str) * 8, '064b')
len_bitstr
'0000000000000000000000000000000000000000000000000000000001011000'

ひとまず、プリント関数を使用してどのように分割すればいいか考えてみましょう。

For now, let's consider how to split it using the print function.

for i in range(len(len_bitstr) // 8):
    print(i, i * 8, (i + 1) * 8)

0 0 8
1 8 16
2 16 24
3 24 32
4 32 40
5 40 48
6 48 56
7 56 64

では先ほどの64ビットの元のメッセージの長さのビット数の文字列を、8つの要素の配列にしていきましょう。

Now let's take the string of bits of the original 64 bit message length and make it an 8 element array.

n = 8
[len_bitstr[i * n:(i + 1) * n] for i in range(len(len_bitstr) // n)]
['00000000',
 '00000000',
 '00000000',
 '00000000',
 '00000000',
 '00000000',
 '00000000',
 '01011000']

先ほどのパディングされたメッセージブロックの配列にextendメソッドを使用して、64ビットの元のメッセージの長さのビット数が含まれた配列をくっつけます。

Use the extend method to concatenate an array containing the number of bits in the original 64 bit message length to the array of padded message blocks in the previous video.

padded.extend([len_bitstr[i * n:(i + 1) * n] for i in range(len(len_bitstr) // n)])

これで512ビット(64バイト)のメッセージブロックを作成することができました。

You have now created a 512 bit (64 byte) message block.

print(f"{len(padded)} bytes")
64 bytes

print(f"{len(padded) * 8} bits")
512 bits

前回同様textwrapモジュールを使用して、配列の中身を分かりすいように表示してみましょう。

As before, let's use the textwrap module to display the elements of the array to make it easier to understand.

textwrap.wrap(" ".join(padded), width=71)
['01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111',
 '01110010 01101100 01100100 10000000 00000000 00000000 00000000 00000000',
 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000',
 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000',
 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000',
 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000',
 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000',
 '00000000 00000000 00000000 00000000 00000000 00000000 00000000 01011000']

続いて、初期値を設定する為に、素数を簡単に扱えるメソッドが使えるsympyモジュールをインストールしましょう。

Next, to set the initial value, you will install the sympy module which has a simple method for handling prime numbers.

初期値はaからhまでの8つです。

The initial value is 8 from a to h.

これは2から始まる3,7,5,11,13,17,19の八つの素数の平方根の小数部分に2の32乗掛け合わせてバイト列にしたものです。

This is the result of multiplying of the fractional parts of the square root of the eight primes, 3, 7, 5, 11, 13, 17, 19, starting at 2, by 2 to the 32 power, into a byte string.

ではpipを使ってsympyモジュールをインストールしましょう。

Now install the sympy module using pip.

python -m pip install --upgrade pip
pip install sympy

isprimeメソッドに整数を入力すれば、その数が素数かどうかの判定をしてくれます。

If you enter an integer into the isprime method, it will determine whether the number is prime.

import sympy

sympy.isprime(0)
False

sympy.isprime(1)
False

sympy.isprime(2)
True

sympy.isprime(3)
True

sympy.isprime(4)
False

sympy.isprime(5)
True

素数とは0と1以外の整数の内、その数と1でしか割り切れない数のことです。

A prime number is an integer other than 0 or 1 that can only be divided by 1 and itself.

では8つの初期ハッシュ値を作成していきましょう。

Let's create eight initial hash values.

まずは空の配列を用意して、その中に八つの素数を格納するところから始めましょう。

Let's start by creating an empty array and then storing eight prime numbers in it.

mathモジュールをインポートしてsqrtメソッドを使用して平方根を求めます。

Import the math module and use the sqrt method to calculates the square root.

そしてmodfメソッドを使用して、平方根の整数部分と少数部分を分割します。

The modf method is then used to split the integer and fractional parts of the square root.

そして、分割した小数部分に対して2の32乗を掛けた値の整数を16進数表記に変換します。

And then multiply that the fractional parts by 2 to the power of 32, then converts the value to an integer and converts it to hexadecimal notation.

ではこれら一連の作業の流れをまとめて行ってみましょう。

Let's summarize these steps.

init_hash_values
['0x6a09e667',
 '0xbb67ae85',
 '0x3c6ef372',
 '0xa54ff53a',
 '0x510e527f',
 '0x9b05688c',
 '0x1f83d9ab',
 '0x5be0cd19']

次に定数です。これは2から始まり、311までの64の素数の立方根の少数部分に2の32乗掛け合わせてバイト列にしたものです。

Next, let's create constants. these are the result of multiplying of the fractional parts of the cube root of the 64 prime numbers from 2 to 311, by 2 to the 32 power.

立方根を計算してみましょう。

Let's calculate the cube root.

numpyのcbrtメソッドでも計算できます。

It can also be computed by numpy's cbrt method.

平方根の計算もmathモジュールを使わなくてもできます。計算量は少ないのでどちらか好きな方を選択してください。

You can also calculate square roots without using the math module. Since the calculation amount is small, please choose whichever you like.

初期定数も初期ハッシュ値の時と同様な方法で作成できます。

The initial round constants can be created in the same way as the initial hash values.

平方根が立方根に変わっていることに注意してください。

Notice that the square root has changed to the cube root.

textwrap.wrap(" ".join(round_constants), width=89)
['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']

今までの作業の流れを図を見ながら復習しましょう。

Let's review the flow of work so far by looking at the diagram.

まず、「hello world」という文字列をバイナリに直してみましょう。

First, let's convert the string [hello world] into binary.

そして、1ビットをバイナリの末尾に加えます。

then, append a single 1 bit to the end of the binary.

さらに、512ビットから64ビットを引いたビット数(今回の場合は448ビット)になるまでゼロでパディングしていきます。

Pad with zeros until the number of bits is 512 bits minus 64 bits (in this case 448 bits).

次に残りの64ビットに元のメッセージの長さのビット数を加えます。

It then adds the number of bits in the original message length to the remaining 64 bits.

88を8ビットのバイナリ文字列に変換すると[01011000]となります。

Converting 88 to an 8-bit binary string is [01011000].

この値を64ビットの長さになるように調節してメッセージブロックに加えていきます。

Adjust this value to a length of 64 bits and add it to the message block.

次に初期ハッシュ値です。これは2から始まる8つの素数の平方根の小数部分に2の32乗(32ビットの整数になるようにする為)して導き出します。

Next are the initial hash values. This is derived by multiplying of the fractional parts of the square root of the eight primes, 3, 5, 7, 11, 13, 17, 19, starting at 2, by 2 to the 32 power (to get a 32 bit integer).

次にラウンド定数です。これは2から始まる64つの素数の立方根の小数部分に2の32乗(32ビットの整数になるようにする為)して導き出します。

Next are the round constants. This is derived by multiplying of the fractional parts of the cube root of the 64 primes, 3, 5, 7, ......., 293, 307, 311, starting at 2, by 2 to the 32 power (to get a 32 bit integer).

つまり先ほど作った初期ハッシュ値をメッセージブロックやラウンド定数を利用することによって、変化させたものが最終的なアウトプットのハッシュ値な訳です。

That is, the hash value of the final output is the result of changing the initial hash values we created by using message blocks and round constants.

八つのハッシュ値は4バイトの16進数が8つあり、32バイトです。そして文字数は64文字であることに気が付くとそのことが理解できるはずです。

The eight hash values consist of 8 4-byte hexadecimal (32 bytes). And when you realize that the number of characters is 64, you should be able to understand that.



以上です。お疲れ様です。

That's all. Thank you for your hard work.