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

012 Pythonで遊ぶ (シャー256 2)
012 Playing with Python (sha256 2)


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.

では、ハッシュアルゴリズムの一つであるsha256の内容の続きを始めましょう。

Let's continue with the contents of one of the hash algorithms, sha256.

パート1からパート4までの動画では、バイナリ操作についての基本的なことを紹介してきました。

In Part 1 through Part 4 of this video series, I explained the basics of binary manipulation.

但し、sha256に関してのより詳しい内容や正確な情報はwikipediaFIPS180-4を参考にしてください。

However, see wikipedia and FIPS180-4 for more details and accurate information on sha256.

簡単に説明すると、sha256はバイト列に対して、予め決められた初期値や定数などを使用してバイナリ操作を何度も行ってハッシュ値を導きだしています。

Briefly, sha256 derives the hash value by performing many binary operations on the byte sequence using predetermined initial values, constants, etc.

さっそくPythonスクリプトを書きながらハッシュアルゴリズムの一つであるsha256がどのように動作しているかを確かめましょう。

Let's start by writing a Python script to see how one of the hash algorithms, sha256, works.

その前にPythonで文字をInt型に変換する作業の復習です。詳しい事はパート1から動画を見てください。

Before that, let's take a quick refresher on converting characters to Int types in Python. For details, see the video from Part 1 to Part 4.

ord('a')
97

format(ord('a'), '08b')
'01100001'

format(ord('a'), '02x')
61

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

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

input_str = "hello world"

converted_str = [format(ord(c), '08b') for c in input_str]

converted_str
['01101000',
 '01100101',
 '01101100',
 '01101100',
 '01101111',
 '00100000',
 '01110111',
 '01101111',
 '01110010',
 '01101100',
 '01100100']

この変換したバイナリ文字列を理解しやすいように表示方法を整えてみましょう。

Let's arrange the display to make this converted binary string easier to understand.

" ".join(converted_str)
'01101000 01100101 01101100 01101100 01101111 00100000 01110111 01101111 01110010 01101100 01100100'

for idx, bits in enumerate(converted_str):
    print(f"{input_str[idx]} -> {bits}")

h -> 01101000
e -> 01100101
l -> 01101100
l -> 01101100
o -> 01101111
  -> 00100000
w -> 01110111
o -> 01101111
r -> 01110010
l -> 01101100
d -> 01100100

ではsha256の検証用の配列にこのバイナリ文字列が入った配列をコピーメソッドを使ってコピーしましょう。

Now use the copy method to copy the array containing this binary string into the sha256 validation array.

[FIPS180-4]に書かれていることを参考にsha256を行う前の準備をしていきましょう。

Let's preprocessing before doing sha256 using [FIPS180-4] as a guide.

因みに今回は分かりやすくするためにバイナリの文字列に変換していますが、実際は"バイナリ"に変換します。バイナリ操作を行わなければならないからです。

This time I convert it to a binary string for clarity, but I actually convert it to "Binary". This is because binary operations must be performed.

第一段階として、バイナリ文字列の最後に1ビットを加えます。

The first step is to add one bit to the end of the binary string.

そして、バイナリ文字列が512ビットになるように余った残りを0で埋めていくパディングを行います。

Then we padding the rest with zeros the binary string to 512 bits.

この段階でのバイナリ文字列の長さを調べてみましょう。

Before we do that, let's examine the length of the binary string at this stage.

89ビットあります。

It has 89 bits.

これから512ビット(64バイト)のメッセージブロックを作成するわけですが、残りの64ビット(4バイト)は元メッセージ(hello world)のビット数を追加するので、空けておきます。

We are going to create a 512 bit (64 bytes) message block, but the remaining 64 bits (4 bytes) add the number of bits in the original message (hello world), so leave them blank.

つまり残り359ビットをゼロで埋めていきます。(512ビットから64ビットを引いた448ビットから、先ほど加えた1ビットと元のメッセージのビット数88ビットを合わせた89ビットを引きます。)

That is, the remaining 359 bits are padded with zeros. (Subtract 89 bits (1 bit plus 88 bits of the original message) from 448 bits (512 bits minus 64 bits).)

パディング用の配列の最後の要素数は1なので、8ビットからその数を引いた7ビットをゼロでパディングします。

The last element of the array used for padding is 1, so 7 bits (8 bits minus that number) are padded with zeros.

その後、残りの352ビット(8ビット掛ける44)をゼロでパディングしていきましょう。

Then we pad the remaining 352 bits (8 times 44) with zeros.

では一連の作業をまとめて行っていきましょう。

Now, let's summarize all of this series of tasks.

小数点の切り上げを行いたいので、mathモジュールからceilメソッドをインポートします。

Import the ceil method from the math module because you want to round up the decimal point.

これにて元のメッセージのバイナリ文字列に1ビットとゼロでパディングされた448ビット(56バイト)のメッセージブロックが作成できました。

You have now created a 448 bit (56 bytes) message block. (with 1 bit and 0 padded in the binary string of the original message.)

ではtextwrapモジュールを使用して、パディングされたバイナリ文字列を見やすいように整形しましょう。

Now let's use the textwrap module to format the padded binary string for easy viewing.

import textwrap

textwrap.wrap(" ".join(padded))
['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']

wrapメソッドのデフォルトの横幅は70文字で設定されているので、8ビットの文字列が横に8つ並ぶように調整してみましょう。

The default width of the wrap method is set to 70 characters, so let's adjust the display that 8-bit strings are displayed in eight columns.

次に残りの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.

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

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

Pythonの配列のスライス機能とForLoopを使って8つに分割していきましょう。

Let's split it into eight pieces using Python's array slicing feature and For Loop.

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

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'


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

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