ナショナルトレジャーに出てきたプレイフェア暗号の片道分を作ってみた。
http://www.disney.co.jp/movies/nt2/
映画の中で出てきた、2文字区切りのアルファベットを見て主人公が「プレイフェア暗号の特徴だ」と言ったのを聞いてしまい。WikiPで調べたら実際にあった。キーワードから文字変換の為にマトリクス表を作成する。マトリクスに従って文章の文字をリプレースしていく。とても簡単そうに思えた。
http://www.tamagaki.com/math/PlayfairCipher.html
に、とても分かりやすくやり方が乗っていたのでやる気になってしまい作ってみた。
やってみると、想像より全然めんどくさい。始めた当初の目的は、さっくり作ってGAE上に秘密鍵ストレージを作ろうとか、WaveのRobotにしてみようと思ったけど、コア部分作るだけで力尽きた。
以下ソース
#!/usr/bin/env python # coding:utf-8 ''' プレイフェア暗号 1: 5文字のキーフレーズを定義する 2: xを使わないキーフレーズ 2.1: IとJを一つのますに入れる(??) 3: キーフレーズを決める 4.1: キーフレーズに使われている文字を全アルファベット内から削除 4.2: キーフレーズの後ろに4.1のアルファベットを追加する 4.3: 5文字5列のマトリックスに分解する ''' cipher = '' def strChecker(key): alphabet = 'abcdefghijklmnopqrstuvwyz' res = [] for alphaStr in alphabet: if alphaStr in key: pass else: res.append(alphaStr) return key + ''.join(res) def makeMatrix(arr): res = [] if len(arr) == 25: for i in range(0,25,5): res.append(tuple(arr[i:i+5])) return res else: return res # 2文字続いている時は間にXを入れる # 文字のトータルが2の倍数になるようにxを最後に付加して調整する def searchDupperStr(orgSentence): orgSentence = orgSentence.lower() orgSentence = orgSentence.replace(' ', '') res = '' for i in range(len(orgSentence)-1): if orgSentence[i] == orgSentence[i + 1]: res += orgSentence[i] + 'x' else: res += orgSentence[i] res += orgSentence[-1] if len(res) % 2 != 0: res += 'x' return res def splitTwo(orgSentence): res = [] return [orgSentence[x] + orgSentence[x+1] for x in range(0, len(orgSentence) -1, 2)] def convUnigram(bygram, sentence, indexOne, indexTwo): global cipher one = sentence[indexOne[0]][indexTwo[1]] two = sentence[indexTwo[0]][indexOne[1]] cipher += "%s%s "%(one, two) return "%s > %s%s\n"%(bygram, one, two) def convHor(bygram, sentence): global cipher one = sentence[(sentence.index(bygram[0])+1)%5] two = sentence[(sentence.index(bygram[1])+1)%5] cipher += "%s%s "%(one, two) return "%s > %s%s\n"%(bygram, one, two) def convXnext(unigram, sentence): return sentence[(list(sentence).index(unigram)+1)%5] def findStr(mxTable, arrStr): global cipher res = '' for i in arrStr: flg = 1 # xが入っていた場合 if i[0] == 'x': for ii in mxTable: if i[1] in ii: cipher += "x%s "%(convXnext(i[1], ii)) res += "%s > %s%s"%(i, 'x', convXnext(i[1], ii)) elif i[1] == 'x': for ii in mxTable: if i[0] in ii: cipher += "%sx "%(convXnext(i[0], ii)) res += "%s > %s%s"%(i, convXnext(i[0], ii),'x') else: # 2文字が横に同列の場合 for ii in mxTable: if i[0] in ii and i[1] in ii: res += convHor(i, ii) flg = 0 # 2文字が縦に位置列の場合 mtx = apply(zip, mxTable) for ii in mtx: if i[0] in ii and i[1] in ii: res += convHor(i, ii) flg = 0 # 2文字がねじれの関係の時 if flg: onePos, twoPos = '', '' for ii in range(len(mxTable)): if i[0] in mxTable[ii]: onePos = [ii, mxTable[ii].index(i[0])] elif i[1] in mxTable[ii]: twoPos = [ii, mxTable[ii].index(i[1])] if onePos and twoPos: res += convUnigram(i, mxTable, onePos, twoPos) print res def makeStr(key, Str): global cipher mtx = makeMatrix(strChecker(key)) strTxt = splitTwo(searchDupperStr(Str)) findStr(mtx, strTxt) print "key : %s"%key print 'original : %s'%Str print 'encript : %s'%cipher print 'conv_text: %s'%(' '.join(strTxt)) print 'matrix :' for i in mtx: print i def hogeTest(): # step1 : アルファベットの並び替え>26文字 step1 = strChecker(keyphrase) # step2 : マトリックス作成 step2 = makeMatrix(step1) step3 = searchDupperStr(originalStr) step4 = splitTwo(step3) print step1 print step2 for i in step2: print i print keyphrase print originalStr print step3 findStr(step2, step4) if __name__ == '__main__': import sys argvs = sys.argv if len(argvs) >1 : keyphrase = argvs[1] originalStr = argvs[2] makeStr(keyphrase, originalStr)
リポジトリ:http://bitbucket.org/a2c/python-playfair/overview/
使い方
キーワードを指定する必要が有る。25文字以内で、アルファベット。文字は1度しか使えない。
- 例
- Good
- lady
- gold
- bad
- baby
- tokyo
- Good
スクリプトの第1引数にキーワード、第2引数に暗号化したい文言を指定し起動する。
python PlayfairCipher.py gold 'time is money'
スペースはつぶされて、2文字以上のつながりがある部分は間にxが入る。
オリジナルの'time is money'は 'ti me is mo ne yx'となる。最後にxが付加されているのは、2の倍数にする為
暗号化の結果は、
key : gold
original : time is money
encript : pn kf mp jd kh zx
conv_text: ti me is mo ne yx
matrix :
('g', 'o', 'l', 'd', 'a')
('b', 'c', 'e', 'f', 'h')
('i', 'j', 'k', 'm', 'n')
('p', 'q', 'r', 's', 't')
('u', 'v', 'w', 'y', 'z')
'time is money' > 'ti me is mo ne yx' > 'pn kf mp jd kh zx' と変化していってる。
キーワードの'gold'さえ覚えておけば、'pn kf mp jd kh zx' を元の文章に戻す事が可能なはず。
昔の暗号は、頭の体操にちょうど良いかも
プログラミングで頭に負荷掛けたくても、自分で考えるお題は、バカの壁の内側なのでたいしたお題を思いつかない。
何百年も前から有る暗号をLLで実装するのはとても頭の体操になる。ネタもいっぱい転がってるしちょうど良いかも知れない。
次は、復号をやってみたいけどちょっと大変そうな気がする。逆をするだけだから簡単かな?
適当すぎるコードだけど、書かないよりましだと思った。