【Python】転置式暗号プログラム

2023年1月11日

Pythonで転置式暗号の暗号化プログラムの解説をします。

転置式暗号とは

転置式暗号は鍵により元のメッセージの順序を入れ換えるものです。

シーザー暗号とは異なり文字自体は変わらず順序だけを変えます。

転置式暗号はどのように暗号化するのかを順を追って説明します。

転置式暗号で暗号化する

今回はメッセージ「If you can dream it, you can do it.」を暗号化させます。

ちなみにこの言葉の意味は「夢を見ることができれば、それは実現できる。」で、ウォルト・ディズニーの名言です。

1.メッセージと鍵の文字数を数える

まず暗号化させたいメッセージの文字数を数えます。

メッセージ「If you can dream it, you can do it.」は35文字になります。

このとき空白や句読点も文字数に含まれます。

次に鍵の文字数を決めます。

鍵の数の候補となる範囲は2からメッセージの半分までとなります。

今回は鍵に数8を使います。

2.鍵と等しい数の箱を一列に並べて書く

次に鍵の数と等しい数の箱を一列に並べて書きます。

今回は鍵の数が8なので8個の箱を一列に書きます。

               

3.左から右に暗号化させる文字を入れる

次に書いた箱の中に暗号化させたいメッセージの文字を1文字ずつ入れています。

このとき左から右に1個の箱に1文字ずつ入れていきます。

空白も文字としてカウントされます(ここでは空白は♦と表記しています)。

I f y o u c

4.箱がなくなり文字が残っていたら箱を追加して入れる

このように暗号化したいメッセージの文字が残っていて箱が足りない場合は、その下の行に鍵の数の箱を追加します。

これを暗号化したいメッセージの文字がすべて埋まるまで続けます。

I f y o u c
a n d r e a m
i t , y o u
c a n d o
i t .          

5.最後の行の未使用の箱に影を付ける

箱に暗号化したいメッセージをすべて入れたとき、一番下の最後の行に文字が入っていない箱がある場合があります。

その場合はその箱に影を入れます。

I f y o u c
a n d r e a m
i t , y o u
c a n d o
i t .          

6.左上から各列を下に読む
一番下に来ると右の列の一番上に移動していく

転置式暗号は左上の箱から下に読み進めていきます。

一番下の箱に到達したら右の列の一番上の箱に移動して同じく下に向かって読み進めます。

その際影を付けた箱は無視していきます。

1列目 2列目 3列目 4列目 5列目 6列目 7列目 8列目
I f y o u c
a n d r e a m
i t , y o u
c a n d o
i t .          

この暗号文の場合は1列目はI、a、♦(空白)、♦(空白)、iになります。

その列の一番下に到達したら右側の一番上に移動します。

次の列つまり2列目はf、n、i、c、tとなります。

影付きの箱は無視するので4列目はy、d、,、nとなり、5列目はo、r、♦(空白)、♦(空白)となります。

暗号文は「Ia  ifnict  ta.yd,nor  ueyd aoocmu 」になります。

転置式暗号の規則性

転置式暗号のプログラムを作るにあたり規則性がないかを見ていきます。

先ほどの鍵8を使って文字列’If you can dream it, you can do it.’を暗号化する方法で考えます。

まずそれぞれの文字に0から始まるインデックスを追加します。

1列目 2列目 3列目 4列目 5列目 6列目 7列目 8列目
I
0
f
1

2
y
3
o
4
u
5

6
c
7
a
8
n
9

10
d
11
r
12
e
13
a
14
m
15

16
i
17
t
18
,
19

20
y
21
o
22
u
23

24
c
25
a
26
n
27

28
d
29
o
30

31
i
32
t
33
.
34
         

最初の1列目はインデックス0、8、16、24、32の文字である’I’、’f’、’♦(空白)’、’♦(空白)’、’i’です。

次の2列目はインデックス1、9、17、25、33の文字である’f’、’n’、’i’、’c’、’t’です。

これらからn列目はインデックス0 + (n – 1)、8 + (n – 1)、16 + (n – 1)、24 + (n – 1)、32 + (n – 1)の文字になるとわかります。

1列目 2列目 3列目 4列目 5列目 6列目 7列目 8列目
I
0
=0+0
f
1
=1+0

2
=2+0
y
3
=3+0
o
4
=4+0
u
5
=5+0

6
=6+0
c
7
=7+0
a
8
=0+8
n
9
=1+8

10
=2+8
d
11
=3+8
r
12
=4+8
e
13
=5+8
a
14
=6+8
m
15
=7+8

16
=0+16
i
17
=1+16
t
18
=2+16
,
19
=3+16

20
=4+16
y
21
=5+16
o
22
=6+16
u
23
=7+16

24
=0+24
c
25
=1+24
a
26
=2+24
n
27
=3+24

28
=4+24
d
29
=5+24
o
30
=6+24

31
=7+24
i
32
=0+32
t
33
=1+32
.
34
=2+32
         

ここで32 + (4 – 1)と32 + (5 – 1)と32 + (6 – 1)と32 + (7 – 1)と32 + (8 – 1)はインデックスにはありません。

文字列の最大のインデックスとなる34より大きいため、4から8列目の最終行が除外されています。

上の表を見ても4から8列目はnに0、8、16、24を追加していますが32は追加せずスキップしています。

この0、8、16、24、32は0から8(鍵の数)を順に加算した値です。

0+8=8、8+8=16、16+8=24、24+8=32となり32+8=40ですがこれはメッセージの文字数を超えるので32までになります。

n列目の文字はインデックスn – 1から始まり、下の行の文字がある箱に移動するため鍵の値である8を加算していきます。

メッセージの文字数である35未満まで8を加算していき、35以上になると次の列の一番上の行に移動します。

これらの規則性を使って転置式暗号のプログラムを作っていきます。

転置式暗号のソースコード

コード例

# 転置式暗号の暗号化

def main():
    """入力されたメッセージと鍵の値で暗号化"""
    message = input('暗号化するメッセージ : ')
    key = int(input('鍵の値 : '))

    ciphertext = encryptMessage(key, message)

    # 画面に暗号文を表示する際に最後が空白の可能性も考慮してパイプ文字を配置
    print(ciphertext + '|')


def encryptMessage(key, message):
    """鍵の数の箱を一列に並べて文字を埋める"""
    ciphertext = [''] * key

    # ciphertextの各列でループ
    for column in range(key):
        currentIndex = column

        # currentIndexがメッセージの文字数を超えるとループ終了
        while currentIndex < len(message):
            # ciphertextの現在位置にmessageにおけるcurrentIndexの位置にある文字を追加
            ciphertext[column] += message[currentIndex]

            # currentIndexを鍵の値だけ移動させる
            currentIndex += key

    # 暗号文のリストを単一の文字に変換して返却
    return ''.join(ciphertext)


if __name__ == '__main__':
    main()

実行結果

暗号化するメッセージ : If you can dream it, you can do it.
鍵の値 : 8
Ia  ifnict  ta.yd,nor  ueyd aoocmu |

defで関数を作る

コード内の2箇所にあるdefは関数を作る際のキーワードです。

前者の関数はメッセージと鍵を入力させ、それらをもとに暗号文に変換して表示させます。

後者の関数では暗号化する作業をさせています。

関数の利点は中身を知らなくても値を渡せば勝手に処理してくれるところです。

この場合でも仕組みを知らなくてもメッセージと鍵の値を渡せば暗号化された文字列が得られます。

表示結果に工夫

このコードでは暗号文の表示結果の最後にパイプ文字’|’を追加しています。

これは最後の文字が空白文字でも識別できるようにしています。

実際に今回の実行例でも最後が空白文字ですが、ちゃんと識別できると思います。

空文字列のリストで一列の箱を再現

関数encryptMessage内では与えられた文字列を鍵を使って暗号化しています。

転置式暗号ではまず鍵の値の数の箱を一列に書くことをしました。

それを再現するのがciphertext = ["] * keyという箇所です。

これはkeyの値ぶんの空文字列を作ることで再現しています。

ciphertextはリストであり、個々の要素は列における文字です。

ciphertext[0]には0列目の文字、ciphertext[1]には1列目の文字といった感じです。

0列目 1列目 2列目 3列目 4列目 5列目 6列目 7列目
I f y o u c
a n d r e a m
i t , y o u
c a n d o
i t .          

実行例の場合に手動でciphertextに格納すると以下のようになります。

ciphertext = ['Ia  i', 'fnict', '  ta.', 'yd,n', 'or  ', 'ueyd', ' aoo', 'cmu ']

ciphertext[0]は’Ia  i’、ciphertext[1]は’fnict’となります。

この手動でやったことをプログラムで実現するようにします。

列ごとにループして箱に文字を格納

for column in range(key):では列ごとに反復します。

range関数は0から始めるのでkeyが8の場合は0から7ということになります。

currentIndex = columnなのでどちらも同じ値からとなります。

forループの最初の反復ではcolumnには0がセットされ、次の反復では1、その次では2というようになります。

これは転置式暗号におけるn列目のnに相当しています。

一方currentIndexはforループの各反復で注目しているmessageのインデックスに相当しています。

この値を変更させることで暗号化が実現します。

文字を攪拌する

while文内ではメッセージを転置式暗号で暗号化する工程をしています。

ciphertext[column] += message[currentIndex]ではリストciphertextにメッセージの文字を格納しています。

columnもcurrentIndexも初期値は0のためメッセージの最初の文字が格納されます。

columnは転置式暗号における何列目かを表すものなので、初期値だと0つまり一番左側の0列目ということになります。

つまり一番はじめは最初の列の1番目にメッセージの最初の文字を格納するということです。

実行例だとciphertext[0]にmessage[0]である’I’を格納します。

その後にcurrentIndex += keyによりcurrentIndexにkeyの値を加算して鍵の値だけスキップさせます。

この状態でciphertext[column] += message[currentIndex]に戻ります。

columnは変化なしで0のまま、currentIndexはkeyが加算された値となります。

これによりciphertext[column]にはメッセージにおけるkeyの値だけスキップされたインデックスの文字が後ろに連結されます。

実行例だと8文字分スキップされたmessage[8]の’a’が連結されciphertext[0]は’Ia’となります。

これらをwhile currentIndex < len(message):によってcurrentIndexがメッセージ長を超えるまでループが続きます。

currentIndexがメッセージ長を超えると条件が成り立たないのでループは終了します。

実行例だとcolumnが0のとき、連結されるのは初回はmessage[0]、2回目はmessage[8]、3回目はmessage[16]、4回目はmessage[24]、5回目はmessage[32]となります。

その結果ciphertext[0]には’Ia  i’が格納されます。

順番 0列目 1列目 2列目 3列目 4列目 5列目 6列目 7列目
1番目 I
0
f
1

2
y
3
o
4
u
5

6
c
7
2番目 a
8
n
9

10
d
11
r
12
e
13
a
14
m
15
3番目
16
i
17
t
18
,
19

20
y
21
o
22
u
23
4番目
24
c
25
a
26
n
27

28
d
29
o
30

31
5番目 i
32
t
33
.
34
         

whileループが終了するとfor文に戻りcolumnは次の値(+1)になってwhileループに入ります。

実行例だとcolumnが1のとき、連結されるのはcolumnが0のときから数えて6回目はmessage[1]、7回目はmessage[9]、8回目はmessage[17]、9回目はmessage[25]、10回目はmessage[33]となります。

その結果ciphertext[1]には’fnict’が格納されます。

0列目 順番 1列目 2列目 3列目 4列目 5列目 6列目 7列目
I
0
6番目 f
1

2
y
3
o
4
u
5

6
c
7
a
8
7番目 n
9

10
d
11
r
12
e
13
a
14
m
15

16
8番目 i
17
t
18
,
19

20
y
21
o
22
u
23

24
9番目 c
25
a
26
n
27

28
d
29
o
30

31
i
32
10番目 t
33
.
34
         

これらをメッセージのすべての文字を格納するまで続けます。

最終的に実行例だとciphertextは['Ia  i’, 'fnict’, '  ta.’, 'yd,n’, 'or  ', 'ueyd’, ' aoo’, 'cmu ']となります。

ただこれは暗号文の各列のリストに過ぎないのでこれらを連結させて暗号文にさせる必要があります。

join()メソッドで連結

ciphertextに格納されているのは暗号文の各列における文字のリストなのでそれらを連結させて単一の文字列にする必要があります。

このとき使用するのがjoin()メソッドです。

".join(ciphertext)でciphertextの各要素を連結させています。

今回は各要素の間には何も入れないようにしています。

こうすることで文字列のリストが単一の文字列に変換されます。

連結された文字列はreturn文により関数encryptMessageの戻り値となります。

変数__name__

if __name__ == '__main__’:はインポートを想定した記述です。

これにより関数main()はこのプログラムを直接実行したときだけ呼び出されます。

他のプログラムでインポートされた際は実行されないので暗号化の部分だけが利用できます。

Python

Posted by ほりえりお