Published Date : 2021年9月8日3:03
ニコニコ動画にアップした動画のまとめ記事です。
This is a summary blog post about a video I uploaded to NicoNico.
細かい部分は動画を参考にしてください。
Please refer to the video for details.
目次
Table of Contents
前回の続きです。
Continued from last time.
seed(r)と呼ばれるランダムなバイト列とMGF(マスク生成関数)の仕組みについての簡単な説明を図を使って行います。
I'll use a diagram to briefly explain random bytes called seed (r) and and how work MGF (mask generation function).
まず、seedもしくはrと呼ばれるランダムなバイト列をMGF(マスク生成関数)の第一引数に入れたとします。
Let's say you put a random byte string called seed or r in the first argument of the MGF (mask generation function).
このseedの長さはどのハッシュ関数を併用するかによって変わります。つまりsha1なら20バイト、sha256なら32バイトになります。
The length of this seed depends on which hash function is used. That is, sha1 is 20 bytes and sha256 is 32 bytes.
このseedの値を使ってDBをマスクします。つまり、(k - hLen - 1)の長さのマスクを生成します。マイナス1バイトはEMの先頭に付け足す1バイト[0x00]です。
Use this seed value to mask the DB. That is, it produces a mask with a length of (k - hLen - 1). Minus 1 byte is 1 byte [0x00] added to the beginning of EM.
まずMGF(マスク生成関数)内で空のバイト文字列のmaskを作成します。
First create a mask for the empty byte string in the MGF (mask generation function).
そして、DBの長さをseedの長さで割った数をceilメソッドを使って切り上げます。ceilメソッドは与えられた数値を最も近い整数に切り上げる働きをします。
Then use the ceil method to round up the DB length divided by the seed length. The ceil method rounds a number up to the nearest integer.
あとはseedの値にForループのカウント数(バイト列に変換する)を足して、その値のハッシュ値をmaskに連結させていきます。
Then it adds the count of the For loop (converting it to a sequence of bytes) to the seed value and concatenates the hash value of that value into mask.
つまり、DBの長さをseedの長さで分割して、丁度DB全体がマスクされるようにします。
That is, the length of the DB is divided by the length of the seed so that the entire DB is just masked.
ceilメソッドを使っているので、長さが溢れた分は配列のスライス機能を使って調整します。(例えばmask[ :DBlength]のように)
Since we're using the ceil method, we'll use the array slicing feature to adjust the excess length. (For example, mask [: DBlength])
それでは、Pythonを使ってMGF(マスク生成関数)の仕組みを理解してみましょう。
Let's use Python to understand how MGF (mask generation function) works.
まずはseedを作成しましょう。
Let's create a seed first.
import secrets hash_length = 32 secrets.randbits(hash_length * 8).to_bytes(hash_length, 'big')
このように、secretsモジュールのrandbitsメソッドを使用してランダムなビット列を生成するのもいいですが、めんどくさいのでosモジュールのurandomメソッドを使用しましょう。
Using the randbits method in the secrets module to generate random bits like this is fine, but it's a bit cumbersome, so use the urandom method of the os module.
import os os.urandom(hash_length) seed = os.urandom(hash_length) print(seed) len(seed)
そして、空のバイト文字列のmaskを作成します。
Then, create a mask for the empty byte string.
mask = b''
次にseedの長さ、つまりハッシュ関数の種類によるハッシュ値の長さであるhLenの値を決めます。
Then determine the length of seed, hLen, which is the length of the hash value according to the hash function type.
hLen = 32
つまり、今回はsha256を使用するので、hLenの長さは32バイトです。
That is, this time we use sha256, so hLen is 32 bytes long.
maskの長さの変数はmLenです。つまり、DBの長さ(k - hLen - 1)の値をこの変数に代入します。今回は仮に227バイトとします。
The variable for mask length is mLen. In other words, assign the value of the DB length (k - hLen - 1) to this variable. Let's say 227 bytes this time.
mLen = 227
そして、DBの長さをseedの長さで割った数をceilメソッドを使って切り上げます。ceilメソッドは与えられた数値を最も近い整数に切り上げる働きをします。
Then use the ceil method to round up the DB length divided by the seed length. The ceil method rounds a number up to the nearest integer.
import math math.ceil(mLen / hLen)
あとはseedの値にForループのカウント数(バイト列に変換する)を足して、その値のハッシュ値をmaskに連結させていきます。
Then it adds the count of the For loop (converting it to a sequence of bytes) to the seed value and concatenates the hash value of that value into mask.
仮にカウント数が0の場合を見てみましょう。
Let's take a look at the case where the count is 0.
バイト列に変換する場合は4バイト長のビッグエンディアンにします。
If converting to a byte string, make it big-endian with a length of 4 bytes.
count = 0 print(count.to_bytes(4, byteorder='big')) count = 1 print(count.to_bytes(4, 'big'))
ではhashlibモジュールをインポートして、sha256を使用してmaskの生成過程を観察してみましょう。
Let's import the hashlib module and use sha256 to observe the mask generation process.
import hashlib print(hashlib.sha256(seed + count.to_bytes(4, 'big')).digest())
int型をバイトオブジェクトに変換する関数をこのサイト(https://datatracker.ietf.org/doc/html/rfc3447#section-4.1)に倣って、作成しましょう。
Let's create a function that converts an int to a byte object, following this site (https://datatracker.ietf.org/doc/html/rfc3447#section-4.1).
def i2osp(int_obj, byte_length): return int_obj.to_bytes(byte_length, 'big') print(i2osp(5, 4))
ceilメソッドを使っているので、長さが溢れた分は配列のスライス機能を使って調整します。
Since we're using the ceil method, we'll use the array slicing feature to adjust the excess length.
for count in range(0, math.ceil(mLen / hLen)): print("*" * 36) print("count:", count) print("mask:", mask) byte_count = i2osp(count, 4) print("seed + byte_count:", seed + byte_count) mask += hashlib.sha256(seed + byte_count).digest() print("next mask:", mask) print("*" * 36) print(mask) print(len(mask)) print(mLen) print(mask[:mLen]) print(len(mask[:mLen]))
ではこの作成したマスクで、xor演算を使ってDBにマスクをしてみましょう。
Let's use this mask to mask the DB using the xor operation.
その前に、先ほどの(k - hLen - 1)の長さのランダムなビット列を作成して、それを検証用の一時的なDBにしましょう。
Before we do that, let's create a random sequence of bits of length (k - hLen - 1) and make it a temporary DB for verification.
このまま先ほど作成したmaskと一時的なDBをXOR演算すると、バイトオブジェクト同士なので例外を発生させます。
If you XOR the mask you just created and the temporary DB, you will raise an exception because they are byte objects.
なので、バイトオブジェクト同士のXOR演算用の関数を作成しましょう。
So let's create a function for XOR operations between byte objects.
バイト配列の要素にアクセスする時はintオブジェクトになるので、それを利用します。
When accessing an element of a byte array, it is an int object, so we use it.
先ほど作成したint型をバイトオブジェクトに変換する関数も活用しましょう。
Let's also use the function we just created to convert an int to a byte object.
これで前回の動画で説明したxor演算を使用して、マスクを外すことができるかどうかを試してみましょう。
Now let's try to see if you can unmask using the xor operation described in my previous video.
以上です。お疲れ様です。
That's all. Thank you for your hard work.