最近、サイモン・シンの「暗号解読(上)」を読んでいる。主にライブ開演待ちの時間に読む程度なのでなかなか読み終わらない。 硬いタイトルだけど、暗号理論の難しい話は無くて紀元前から始まる暗号にまつわるエピソードがメインになっている。この手の科学読み物的な本はかなり好み。 シーザー暗号やらヴィジュネル暗号、エニグマまでが今のところ登場していて、それぞれのエピソードと並んでアルゴリズムについても触れられている。というわけで今回はシーザー暗号をPythonで書いてみる。
シーザー暗号 is 何
ユリウス・カエサルが使ったことからシーザー暗号と呼ばれている。
"A" →"C", "B" → "D" というように平文のアルファベットに常にひとつのアルファベットが対応する単一換字式暗号という種類の暗号。平文に含まれるアルファベットを平アルファベット
、暗号に含まれるアルファベットを暗号アルファベット
と呼ぶ。
シーザー暗号では、平文を暗号化するときに各文字をアルファベット順で何文字シフトするか、というのを鍵にする。
例えば、鍵が2であるときHello world
は各文字を2つシフトさせて jgnnqyqtnf
となる。
このときの平アルファベットと暗号アルファベットの対応は以下のようになる。
平アルファベット | a | b | c | d | E | ... |
---|---|---|---|---|---|---|
暗号アルファベット | C | D | E | F | G | ... |
Pythonでシーザー暗号
文字をシフトさせると聞いて、初めに思いついたのがord()
でUnicodeコードポイントに変換、そこに鍵となる値を足し、chr()
で文字列に戻すという方法だった。
>>> ord('a') 97 >>> chr(ord('a')+2) 'c'
しかし、この場合z
を暗号化するとUnicodeでこれらの文字の2文字隣にコードされている文字(=|
)になってしまって具合がよくない。
>>> chr(ord('z')+2) '|'
そこで、string.ascii_lowercase
を使ってアルファベット26文字を取得。これを平アルファベットとする。
暗号アルファベットはself.plain_alphabet[self.key:] + self.plain_alphabet[:self.key]
のように、平アルファベットのkey
文字目移行のスライスと、key
文字目までのスライスを組み合わせて作る。暗号化は平文の各文字の平アルファベット上の位置を、暗号アルファベット上の位置に置き換えることで行う。
import string from typing import List class Caesar: def __init__(self, msg: str, key: int): self.msg = msg self.key = key @property def plain_msg(self) -> str: return ''.join( [c.lower() for c in self.msg if c.lower() in self.plain_alphabet] ) @property def plain_pos(self) -> List: alphabet_dict = {} for idx, c in enumerate(self.plain_alphabet): alphabet_dict[c] = idx return [alphabet_dict[c] for c in self.plain_msg] @property def plain_alphabet(self) -> str: return string.ascii_lowercase @property def encrypted_alphabet(self) -> str: return self.plain_alphabet[self.key:] + self.plain_alphabet[:self.key] def encrypt(self) -> str: # ここでは暗号は大文字で表す return ''.join( [self.encrypted_alphabet[pos] for pos in self.plain_pos] ).upper() def decrypt(self) -> str: # ここでは平文は小文字で表す return ''.join( [self.plain_alphabet[pos] for pos in self.plain_pos] ).lower()
アルファベット26文字すべて登場することから、暗号化がわかりやすくなるのでフォントの例文によく使われるアレを使う。こういうのをパングラムって言うらしい。
鍵は2なので t
は 2文字シフトしてV
に、h
も同様に2文字シフトしてJ
に暗号化されている。、z
は一周してB
に、y
はA
に暗号化されている。
>>> from caesar import Caesar >>> msg = 'The quick brown fox jumps over the lazy dog' >>> key = 2 >>> caesar = Caesar(msg=msg, key=key) >>> cipher = caesar.encrypt() >>> cipher 'VJGSWKEMDTQYPHQZLWORUQXGTVJGNCBAFQI' >>> plain = caesar.decrypt() >>> plain 'thequickbrownfoxjumpsoverthelazydog'
というわけでシーザー暗号をPythonで書いてみる話だった。
初心者の課題とかにもいいかも、とか思った。