シーザー暗号
シーザー暗号 - Wikipedia
アルファベットを指定された文字数分ズラすという、原始的な暗号化手法です。
最も単純な換字式暗号の一つであり、文字から文字に1文字単位で変換する方式(単純換字暗号)だそうです。
暗号というよりは、いわゆる「鏡文字」みたいなものなので、まじめに暗号化するものとは少し違うかと思われます。
実装(アルファベット・数字のみ)
アルファベット、数字のみの場合は、pythonの組み込み関数のROT13を使えばできます。
codecs
モジュールをインポートしてcodecs.decode()
を使って変換します。
Wiki曰く、伝統的にこういう場合はシフト数は13らしいです。
13にしておけば、アルファベットは26文字なので、同じ関数で暗号化と複合化をこなせるからかと思われます。
ちなみにシーザー暗号ではシフト数は3だったそうですね。
import codecs
codecs.decode('Hello, World!', 'rot13')
# 'Uryyb, Jbeyq!'
codecs.decode('Uryyb, Jbeyq!', 'rot13')
# 'Hello, World!'
実装(漢字含む)
今回は、漢字を含む文字列のデータのマスキングが必要だったので、自前で作りました。
ただし、以下の通りの制約を設けました。
- 制約
- 記号 そのまま(変換しない)
- アルファベット 数字はkeyで設定した文字分だけズラす。ズラす先はループして指定(X→key:5→C)
- かな・カナ・漢字 1文字以上(次に存在する文字が存在するまで※)
※かなカナ漢字は、keyの数字に関係なく1文字ズラすコードにしています。理由は後述します。
以下、実装したコードです。
plaintextの部分に文字列を渡して、出力として暗号化された文字列が返ってくる関数です。
keyの情報は関数内に入れ込みました。
def caesar(plaintext):
key=13 # アルファベットおよび数字について文字コードをズラす数を決める
enc="cp932" # 文字コードを指定する
ciphertext = "" # 暗号化した文字を1文字ずつ追加していくための変数を定義しておく
for ch in list(plaintext): #1文字ずつ走査し、if分を使って文字コードの数字から文字の種類を判定する
#記号
if (' ' <= ch <= '/') or (':' <= ch <= '@') or ('[' <= ch <= '`') or ('{' <= ch <= '~') or ('、' <= ch <= '◯') :
ciphertext +=chr(ord(ch))
#A-Z
elif 'A' <= ch <= 'Z':
ciphertext += chr((ord(ch) - ord('A') + int(key)) % 26 + ord('A'))
#a-z
elif 'a' <= ch <= 'z':
ciphertext += chr((ord(ch) - ord('a') + int(key)) % 26 + ord('a'))
#0-9
elif '0' <= ch <= '9':
ciphertext += chr((ord(ch) - ord('0') + int(key)) % 10 + ord('0'))
#その他(ひらがなカタカナ漢字など)
else:
byte=bytearray(ch.encode(enc)) # 文字コードを指定してエンコードする、この出力は2バイトのbyte列になるのでbytearrayに変換してから扱う
while(1): # 文字が存在する文字コードにぶつかるまで、1ずつバイトをズラしていく
try:
try:
byte[-1]+=0x01 # 末尾バイト部分を1bitズラす、
except:
byte[-1]=0x00 # 繰り上がり考慮。末尾バイトを00に戻して次バイトをインクリメントする
byte[-2]+=0x01
x=byte.decode(enc) # encでデコードしてみて、エラー発生を検知する
except:
pass
else:
break
ciphertext += x
return ciphertext
補足
漢字は次に存在する文字までズラす、という意味を説明します。
漢字の場合、次の番地に文字が定義されていないケースがあります。
たとえば、文字が存在しない文字コードの例が以下です。
この場合、"入"のとなりの番地には文字が指定されていません。
このとき、文字コードを+1してからその文字コードを文字に直そうとすると(0x93FC→0x93FD)、
0x93FDという文字は存在しないため、エラーとなってしまいます。
したがって、文字が存在する番地になるまで、+1を繰り返す、という動作を加えています。
この場合、"入"は"如"になります。
ただし、今回はズラす量を1と想定しているので、これ以上なにもしていませんが、
もし2以上にしたい場合、「"乳"も"入"も、"如"に暗号化されてしまう」みたいな状況になるので、相応の改変してください。
この関係上、かなカナ漢字は「keyで指定した文字数」に関係なく「1つだけ隣にズラす」というプログラムになっています。
とにかく、ズレて戻せさえすればそれでよかったのでそうしました。
※もろもろの判定にtry-except構文を使っているのはさすがにひどいような気がします。
取り急ぎ仕事で動けばよかっただけなので、だれか追記お願いします。