0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

エニグマ暗号をPythonで実装した

Last updated at Posted at 2024-02-26

1. 動機

以下のページを読んで,Pythonで実装できそうだと思ったこと.

たった1枚の紙でナチス・ドイツの傑作暗号機エニグマを再現できる「Paper Enigma Machine」を使ってみた - GIGAZINE

2. 方針

  1. 3つのローターを左から順に1, 2, 3とし,ローター1の左側,ローター1の右側,ローター2の左側,……,ローター3の右側を表す6つのリストを定義する.

  2. 入力された文字列をリストに格納し,先頭から1文字ずつ次の操作を行う.

    a. その文字 $ l_1 $ が,アルファベットの何番目であるか求める.($ n_1 $ とする)
    b. ローター1の右側で同じく $ n_1 $ 番目にある文字 $ l_2 $ を求める.
    c. $ l_2 $ がローター1の左側で何番目であるか求める.($ n_2 $ とする)
    d. ローター2の右側で同じく $ n_2 $ 番目にある文字 $ l_3 $ を求める.
    ……
    g. $ l_4 $ がローター3の左側で何番目であるか求める.($ n_4 $ とする)
    h. リフレクターで同じく $ n_4 $ 番目にある文字 $ l_5 $ を求める.
    i. $ l_5 $ がリフレクターで何番目であるか求める.($ n_5 \ (\neq n_4) $ とする)
    j. ローター3の左側で同じく $ n_5 $ 番目にある文字 $ l_6 $ を求める.
    ……
    p. アルファベットの $ n_8 $ 番目の文字 $ l_9 $ を求める.

3. 実装

from collections import deque

rt1_l = deque([_ for _ in range(26)]); rt2_l = deque([_ for _ in range(26)]); rt3_l = deque([_ for _ in range(26)])
rt1_r = deque([4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9])
rt2_r = deque([0, 9, 3, 10, 18, 8, 17, 20, 23, 1, 11, 7, 22, 19, 12, 2, 16, 6, 25, 13, 15, 24, 5, 21, 14, 4])
rt3_r = deque([1, 3, 5, 7, 9, 11, 2, 15, 17, 19, 23, 21, 25, 13, 24, 4, 8, 22, 6, 0, 10, 12, 20, 18, 16, 14])
rf = [0, 1, 2, 3, 4, 5, 6, 3, 8, 9, 10, 6, 12, 10, 12, 8, 4, 1, 5, 19, 2, 21, 21, 9, 0, 19]
alp_list = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ ')
lkup = lambda lst: alp_list[lst[0]]
b26 = lambda num: len(str(int(str(num), base = 26))) - 1

#各ローターの初期配置を決定する
print('\n' + 'Input three natural numbers you like with a space inbetween. (e.g. 2 7 18)')
print('\n' + 'To load the default setting, just press the ENTER key.' + '\n')
while True:
    rc = input('INPUT> ') #rc:rollcount
    if len(rc) == 0:
        rc1 = 12; rc2 = 2; rc3 = 10
        break
    try:
        rc1 = rc.split(' ')[0]; rc2 = rc.split(' ')[1]; rc3 = rc.split(' ')[2]
        rc3 = int(rc3) % 26; rc2 = int(rc2) + b26(rc3); rc1 = int(rc1) + b26(rc2)
    except IndexError:
        print('\n' + 'ERROR: There are less than three numbers.' + '\n')
        continue
    except ValueError:
        print('\n' + 'ERROR: Non-natural number/character(s) detected.' + '\n')
        continue
    else:
        break
rt1_r.rotate(-rc1); rt1_l.rotate(-rc1); rt2_r.rotate(-rc2); rt2_l.rotate(-rc2); rt3_r.rotate(-rc3); rt3_l.rotate(-rc3)
print('\n' + 'Setting successfully applied.')
print('\n' + 'R1 R2 R3' + '\n' +lkup(rt1_l) + lkup(rt1_r), lkup(rt2_l) + lkup(rt2_r), lkup(rt3_l) + lkup(rt3_r))

#暗号化・複合化を繰り返せるようにするための無限ループ
while True:
    output = ''
    print('\n' + 'Type a word/sentence to be encrypted/decrypted using alphabets, and, spaces if preferred.' + '\n')

    #入力された文字列が1文字以上のアルファベットとスペースのみで構成されていることを確認する
    while True:
        sent = list(input('INPUT> ').upper())
        try:
            ltrs_list = [_ for _ in sent if _ in alp_list]
        except:
            print('\n' + 'ERROR: Invalid character(s) detected.' + '\n')
            continue
        else:
            pass
        if len(ltrs_list) == ltrs_list.count(' '):
            print('\n' + 'ERROR: There are no alphabets.' + '\n')
        else:
            break

    print('\n' + 'R1 R2 R3')
    for j in range(len(ltrs_list)):
        n = alp_list.index(ltrs_list[j])
        if n == 26:
            output += ' ' #スペースは暗号化・複合化しない
        else:
            rc3 += 1; rt3_r.rotate(-1); rt3_l.rotate(-1) #初めに右のローターを一つ回す
            if rc3 % 26 == 22:
                rc2 += 1; rt2_r.rotate(-1); rt2_l.rotate(-1) #右のローターが一回転したら中央のローターを一つ回す
            if rc2 % 26 == 5:
                rc1 += 1; rt1_r.rotate(-1); rt1_l.rotate(-1) #中央のローターが一回転したら左のローターを一つ回す
            rc1 = rc1 % (26 ** b26(rc1)); rc2 = rc2 % (26 ** b26(rc2)); rc3 = rc3 % (26 ** b26(rc3))
            rf_in = rt1_l.index(rt1_r[rt2_l.index(rt2_r[(list(rt3_l).index(rt3_r[n]))])])
            for rf_out in range(26):
                if rf_out != rf_in and rf[rf_out] == rf[rf_in]:
                    result = alp_list[rt3_r.index(rt3_l[rt2_r.index(rt2_l[rt1_r.index(rt1_l[rf_out])])])]
                    output += result
                    print(lkup(rt1_l) + lkup(rt1_r), lkup(rt2_l) + lkup(rt2_r), lkup(rt3_l) + lkup(rt3_r), ltrs_list[j], ' -> ', result)
    print('\n' + 'RESULT: ' + output + '\n' * 2 + 'You can continue with the current (not the initial) setting.')
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?