転置式暗号 -アナグラム-“ヴォルデモート卿”の本名を見破る 【ANGOUのすゝめ】#2

アナグラム:私はヴォルデモート卿

前回の講義で、暗号の分類について学びました。今回からは、より具体的な暗号の仕組みに踏み込んでいきます。 まずは、皆さんがよく知る映画『ハリー・ポッター』の有名なシーンから始めましょう。

  • 課題文字列 : TOM MARVOLO RIDDLE (ヴォルデモート卿の本名)
  • 操作: 文字を並べ替えて、いい感じの英語の文章にしてみてください。
転置暗号(アナグラム)の有名な例、A am Load VOLDEMORTとトムリドルの対応図

答えは I AM LORD VOLDEMORT ですね。 「なーんだ、ただの言葉遊びか」と思いましたか? 実はこれ、立派な古典暗号の一つ。「転置式暗号(Transposition Cipher)」と呼ばれる技術の基礎なのです。

(映画を見直して確認したので、並び替えの対応はあってるはずです。(たぶん))

転置式暗号とは?

 転置式暗号とは、”転置”というように平文の文字の「種類」は変えず、特定のルールに基づき、文字などを「並び順」を変える暗号のことを言います。

転置式暗号のカギの表現方法

 転置方式では、その置き換え順が鍵になるので、\(\pi(i) = j\)で\(i\)番目の文字を\(j\)番目と置き換えるというような表現で鍵\(K\)を表します。

\[ K = (\pi(1), \pi(2), \pi(3),…. \pi(d-1), \pi(d)) \]

例えば、ブロックの文字数\(d= 5\)で 鍵\(K = (3,5,2,1,4)\)の時は、平文を5文字ずつに分割して、並び替えると暗号化できます。

  元の位置: 12345 12345 12

平文  : CRYPT OGRAP HY

  元の位置: 35214 35214 21

暗号文 : YTRCP RPGOA YH

並べ替え変換手順

  • 鍵: 3番目→5番目→2番目→1番目→4番目 の順に取る。
  • 平文: CRYPT OGRAP HY
  1. [1ブロック目: CRYPT] 3文字目(Y) → 5文字目(T) → 2文字目(R) → 1文字目(C) → 4文字目(P) 結果:YTRCP
  2. [2ブロック目: OGRAP] 同様に並べ替え 結果:RPGOA
  3. [3ブロック目: HY] 文字が足りない部分はそのまま 結果:YH
  • 最終的な暗号文: YTRCP RPGOA YH

転置式暗号の安全性

転置式暗号の安全性は、とても低いです。解こうと思えば、簡単に転置式暗号であることが解読者にわかってしまいます。

具体的な方法としては、文字の出現頻度を見ます。英文であれば、統計的に文字の出現頻度が以下の様になっていることが知られています。

文字出現率
E13%
TAONIRSH9%〜6%
DLUCM4%〜3%
PFYWGBV2%〜1%
KXJQZ0.5%〜0.1%

暗号を見て、出現頻度を確認したら、上記と似通っていれば、文字はそのままで並び替えただけであることがわかります。

転置式暗号を解読する。

【解読のカギ】ブロック長\(d\)の目星をつける

転置式暗号であるとわかったら、次に、解読のために、何文字単位(ブロック長\(d\))で入れ替えられたのかを知る必要があります。

これには主に2つの方法があります。

1.因数分解する

転置手法によりますが、スキュタレーや暗号文が長方形に綺麗に収まるものがあります。

  • 手法:文字数がNだとするとその因数をブロック長の候補とします。
  • 例: \( N = 30\) なら 15, 10, 6, 5, 3, 2, 1が候補になります。

スキュタレー(ギリシャ語でσκυτάλη、バトンの意)とは転置式暗号して用いられた道具である。軸にリボン状の羊皮紙を巻き付け、通信文をその羊皮紙に書き込む。古代ギリシアやスパルタ人に使われていた。

2.2文字単位や3文字単位に分けてみて推定

 2文字単位の文字列をバイグラム、3文字単位の文字列をトリグラムと言いますが、暗号文をバイグラムやトリグラムでとりあえず分割してみて、特徴的な並びがあるかどうかで判定する方法です。

例えば、転置暗号であると推定したときに英語であるとわかっていたとすると、”HE”や”LL”(will tell etc.)のような並びが出てきやすいです。

  • 仲良しペア (High Frequency Bigrams): TH, HE, IN, ER, AN
  • 仲が悪いペア (Low Frequency Bigrams): QJ, ZP, VK, JQ

このスコアから、ブロック長を推定します。これは、プログラム的に高い精度で解けます。

暗号英文解読プログラム

 理論的にはわかっても、百聞は一見に如かずということで、英文の転置暗号の解読プログラムを作りました。

テスト用の暗号化文

元の文字列: HELLO WORLD THIS IS A SECRET MESSAGE

暗号化された文字列: HWTSRSEOHAESLRISTALLSEMGODICEE

使用された鍵(ブロック長): 5

暗号解読プログラム

試せる環境(google colabなどが楽)があれば試してみてください。

長いため自動で折りたたまれるときがあります。”ソースを表示”をおせば展開します。

# 転置式暗号解読くん (Transposition Cipher Hacker)
# 英語のバイグラム(2文字の連なり)頻度を利用して解読を試みる

def get_english_score(text):
    """
    解読した文章が「どれくらい英語らしいか」を採点する関数
    """
    # 英語で頻出するバイグラム(2文字の組み合わせ)とトリグラム
    # 頻度が高いものほど加点する
    common_phrases = [
        'THE', 'AND', 'ING', 'ION', 'ENT', # 3文字
        'TH', 'HE', 'IN', 'ER', 'AN', 'RE', 'ON', 'AT', 'EN' # 2文字
    ]
    
    score = 0
    text_upper = text.upper()
    
    for phrase in common_phrases:
        # そのフレーズが登場する回数をカウントしてスコアに加算
        count = text_upper.count(phrase)
        score += count
        
    return score

def decrypt_transposition(ciphertext, key):
    """
    指定された「列数(key)」で転置式暗号を復号する関数
    (長方形に並べて縦に読む処理の逆を行う)
    """
    import math
    
    # 1. 行数を計算する
    # 天井関数(ceil)を使って、文字数 ÷ 列数 を切り上げる
    num_of_columns = math.ceil(len(ciphertext) / key)
    
    # 2. グリッド(表)の作成
    # シェーディング(空白マス)の数を計算
    num_of_shaded_boxes = (num_of_columns * key) - len(ciphertext)
    
    # 文字を配置するリスト
    plaintext = [''] * num_of_columns
    
    # 列と行のポインタ
    col = 0
    row = 0
    
    for symbol in ciphertext:
        plaintext[col] += symbol
        col += 1
        
        # 端まで来たか、あるいは空白マス(文字がない部分)に来たら折り返す
        if (col == num_of_columns) or \
           (col == num_of_columns - 1 and row >= key - num_of_shaded_boxes):
            col = 0
            row += 1
            
    return ''.join(plaintext)

# --- ここからメイン処理 ---

# --- 転置式暗号化のテスト ---
# 元の文字列: HELLO WORLD THIS IS A SECRET MESSAGE
# 暗号化された文字列: HWTSRSEOHAESLRISTALLSEMGODICEE
# 使用された鍵(ブロック長): 5
cipher_text = "HWTSRSEOHAESLRISTALLSEMGODICEE"

print(f"暗号文: {cipher_text}")
print("-" * 40)

# 鍵(ブロック長)を 2 から文字数の半分まで総当たりする
best_score = -1
best_result = ""
best_key = 0

for key in range(2, len(cipher_text) // 2 + 1):
    # 復号を試す
    decrypted_text = decrypt_transposition(cipher_text, key)
    
    # 英語らしさを採点
    score = get_english_score(decrypted_text)
    
    print(f"鍵 {key:2d} の場合: {decrypted_text} (スコア: {score})")
    
    # 最高スコアを更新したら記録
    if score > best_score:
        best_score = score
        best_result = decrypted_text
        best_key = key

print("-" * 40)
print(f"★ 解読予想 ★")
print(f"鍵(列数): {best_key}")
print(f"平文: {best_result}")

出力結果

出力結果を見ると、暗号解読できたことが確認できますね。

暗号文: HWTSRSEOHAESLRISTALLSEMGODICEE
----------------------------------------
鍵  2 の場合: HSWTTASLRLSSEEOMHGAOEDSILCREIE (スコア: 1)
鍵  3 の場合: HESWSETLMSRGRIOSSDETIOACHLEALE (スコア: 1)
鍵  4 の場合: HHTGWAAOTELDSSLIRLSCSREEEIMEOS (スコア: 1)
鍵  5 の場合: HELLOWORLDTHISISASECRETMESSAGE (スコア: 3)
鍵  6 の場合: HSESSDWESTEITOLAMCSHRLGERAILOE (スコア: 1)
鍵  7 の場合: HSEILMIWESSLGCTOLTSOESHRAEDERA (スコア: 1)
鍵  8 の場合: HRHLTSOCWSARAEDETEEILMIESOSSLG (スコア: 0)
鍵  9 の場合: HRHLSLEOCWSARTLMDETEEIASGIESOS (スコア: 0)
鍵 10 の場合: HSEALSLEOCWROERTLMDETSHSIASGIE (スコア: 1)
鍵 11 の場合: HSEALSLEOIEWROERTLMDCETSHSIASG (スコア: 1)
鍵 12 の場合: HSEALSLSMOIEWROERTLEGDCETSHSIA (スコア: 1)
鍵 13 の場合: HSEALITLSMOIEWROERSALEGDCETSHS (スコア: 1)
鍵 14 の場合: HSEHELITLSMOIEWROASRSALEGDCETS (スコア: 1)
鍵 15 の場合: HTREHELITLSMOIEWSSOASRSALEGDCE (スコア: 2)
----------------------------------------
★ 解読予想 ★
鍵(列数): 5
平文: HELLOWORLDTHISISASECRETMESSAGE

まとめ

 転置式暗号について紹介しました。転置式暗号は、単純に文字を入れ替えるものであるため、単純に実装できますが、その分、ちょっとしたプログラムで解けてしまうくらい脆弱です。

次回紹介する、換字式暗号・シーザー暗号では、文字を置き換える暗号です。文字そのものを置き換えてしまうこの暗号は、転置式と比べてどう強いのか?また、 どう数学的に解読するのか?

次回、シーザー暗号編でお会いしましょう。

シーザー暗号について、その仕組みについて図を用いながら紹介。

換字式暗号-シーザー暗号-【ANGOUのすゝめ】#3

古典暗号の代名詞「シーザー暗号(シフト暗号)」を解説。『2001年宇宙の旅』やマフィアの通信に使われた歴史から、合同式(mod)を用いた数学的定義、総当たり攻撃による解読法まで。RSA暗号開発者からの挑戦状も紹介します。

換え字式暗号と転置暗号の理論紹介記事シリーズサムネイル 5 記事
シリーズ

ANGOUのすゝめ 一 ─暗号の始まりと転置─

暗号の分類と転置暗号を理解する