ストリーム暗号について

#はじめに
こんにちは，keygoroと申します．就職活動が本格化してきました．研究と就職活動をバランス良く行うのは難しいですね．
さて，この記事では，まず暗号を扱う際に用いられる基本的な用語を説明します．その後ストリーム暗号について説明し，簡単な暗号プログラムを示します．よろしくお願い致します．

#暗号に使われる用語

###送信者，受信者，盗聴者
登場人物の名前をアリス，ボブ，イブとする．　　
アリスがボブにメールを送るという状況を考えよう.メールを送っているアリスを送信者，メールを受け取っているボブを受信者と呼ぶ．
このとき，メールが送られる途中でイブが何らかの方法でメールを盗み見たとすると，イブを盗聴者と呼ぶ．

###平文，暗号文，復号
暗号化する前のメッセージを平文，暗号化した後のメッセージを暗号文と呼ぶ．また，暗号文を平文に戻すことを復号という．

###共通鍵暗号
暗号化鍵と復号鍵が等しい暗号方式である．何らかの方法で送信者と受信者の間で鍵を共有する必要がある．今回のメインテーマであるストリーム暗号は，共通鍵暗号の１つである．

###公開鍵暗号
暗号鍵と復号鍵が異なる暗号方式である．暗号化鍵を公開し，復号鍵を秘密にしておく．代表的な公開鍵暗号として，RSA暗号がある．

###ビット，バイト
bitは「情報の最小単位」で、「0か1か」という$2$通りの情報を$1$bitで表現できる．
同様に，$2$bitは$2^2$種類，$3$bitは$2^3$種類，$n$bitは$2^n$種類の情報を表現できる．  
逆に，$m$種類の情報は，$n=\log_2{m}$ bitで表せる．
尚、$\log$の底を$２$でなく、$10$にしたときは$n'=\log_{10}{m}$ decit,自然対数の底$e$にしたときは$n''=\log_e{m}$ natと呼ぶ。  
ここで，$m$と$n,n',n''$の関係を求めると，以下のようになる． 
$$
m=2^n=10^{n'}=e^{n''}
$$
であり，
$$
n=n'\log_2{10}
$$

$$
n=n''\log_2{e}
$$

という関係が成立する．

###疑似乱数
疑似乱数は，見かけ上ランダムな系列であるが，seed値からアルゴリズムに基づいて生成される乱数系列である．そのため，seed値を送信者と受信者で共有すれば，擬似乱数系列を共有できる．乱数はコイントスや物理現象を利用して生成することもできるが，手間や装置をつくるのが大変である．そのため，一般的に疑似乱数が多く用いられる．

疑似乱数を発生する装置のことを，疑似乱数発生器(pseudorandom number generator，PRNG)という．

###排他的論理和(XOR)
排他的論理和(XOR,exclusive or)は，ビット列を処理する際に用いられる演算である．$\oplus$という記号で表記する．

1ビットのXORは以下のようになる．  

$0 \oplus 0=0$  

$0 \oplus 1=1$  

$1 \oplus 0=1$  

$1 \oplus 1=0$

#ストリーム暗号

ストリーム暗号は， 平文をビットもしくはバイト単位で暗号化を行う方式である． 　　

平文を$X=x_1x_2\cdots$，共通の秘密鍵を用いて擬似乱数発生装置で生成された擬似乱数系列(鍵ストリームという)を$Z=z_1z_2\cdots$，暗号文を$C=c_1c_2\cdots$とすると，ビットごとに$c_i=z_i\oplus x_i$を計算することで，暗号文$C$が得られる．また，復号する際には，$x_i=z_i \oplus c_i$を計算することで，平文$X$が得られる．
今回，seed値を共通鍵として疑似乱数発生器で鍵ストリームを生成し，平文と1ビットずつ足していく暗号化のプログラムと，復号のプログラムを作成した．以下がそのプログラムである．

#プログラム

```python
#暗号化のプログラム
import numpy as np

def Encrypt(m):#暗号化の関数を定義
    C=[]#暗号文系列(8行目のfor文で排他的論理和をとった値をここに加える．)
    np.random.seed(m)#seed値を入力し乱数を固定する．
    Z= np.random.randint(0, 2, len(X))#平文Xと同じ長さの{0,1}の乱数系列を出力する.

    for i in range(len(X)):#平文と乱数系列で1ビットごとに排他的論理和をとっている．
      C.append((X[i]+Z[i])%2)#平文と乱数系列の排他的論理和をC[]に入れている．
    print("--------暗号化--------")
    print("平文X:",X)
    print("鍵ストリームZ:",Z)
    print("暗号文C:",C)
```

平文系列を入力し，関数Encryptを使って暗号化すると以下のようになる．

```python
X=[1,0,0,0,1,1,1,0,0]#平文を入力
Encrypt(2)#上で定義した関数でseed値を指定する
#出力
--------暗号化--------
平文X: [1, 0, 0, 0, 1, 1, 1, 0, 0]
鍵ストリームZ: [0 1 1 0 0 1 0 1 0]
暗号文C: [1, 1, 1, 0, 1, 0, 1, 1, 0]
```

```python
#復号のプログラム

def Decrypt(m):#復号の関数を定義
    X=[]#平文系列
    np.random.seed(m)#seed値を入力し乱数を固定する．
    Z= np.random.randint(0, 2, len(C))#平文Xと同じ長さの{0,1}の乱数系列を出力する.

    for i in range(len(C)):#暗号文と乱数系列で1ビットごとに排他的論理和をとっている．
      X.append((C[i]+Z[i])%2)#平文と乱数系列の排他的論理和をX[]に入れている．
    print("--------復号--------")
    print("暗号文C:",C)
    print("鍵ストリームZ:",Z)
    print("平文X:",X)
```

平文系列を入力し，関数Encryptを使って暗号化すると以下のようになる．

```python

C=[1, 1, 1, 0, 1, 0, 1, 1, 0]
Decrypt(2)#暗号化と共通のseed値を指定し，復号する
＃出力
--------復号--------
暗号文C: [1, 1, 1, 0, 1, 0, 1, 1, 0]
鍵ストリームZ: [0 1 1 0 0 1 0 1 0]
平文X: [1, 0, 0, 0, 1, 1, 1, 0, 0]
```

#おわりに
用語の解説とストリーム暗号のプログラムをまとめました．なにかのお役に立てば幸いです．

#参考文献
[1] Douglas Robert Stinson, Maura Paterson Cryptography: Theory and Practice, Third Edition (Discrete Mathematics and Its Applications)
[2]結城浩 暗号技術入門 第3版: 秘密の国のアリス