0番染色体

科学は全世界を照らす光である

TeXで暗号化(1) シーザー暗号

今回の記事では「TeXで文書を暗号化してみよう!」という"遊び"を紹介します.第一回の本記事では,極めて古典的な暗号であるシーザー暗号を扱います.

TeXマクロの紹介は東大TeX愛好会の企画今週のマクロでも行っていますが,向こうではTeX/LaTeXの使い方や発想を演繹して活用してもらうことをその主旨としているのに対し,本ブログではTeXを利用して何かをすることに主眼をおいた記事を書くことにしようと思います.要するに,こっちではTeXを使ってより"くだらないこと"に取り組んでみようというわけです.

なお,この記事の一部はTeX言語を扱ったことのある人向けに少々不親切な説明をしているので,難しいと感じた場合はその部分を読み飛ばして頂いても結構です.

シーザー暗号とは

「アルファベットの各文字を n文字右の文字に置き換える暗号」のことをシーザー暗号と呼びます.ただし, n文字右にずらす過程で"z"まで到達してしまった場合はaに戻ってさらに右にずらし続けます.この暗号の名前は,「古代ローマのシーザーがアルファベットを3文字ずつずらす暗号を用いた」とする歴史家スエトニウスの記述に由来しているそうです *1

ここで暗号学の基本的な用語について少し確認しておくと,暗号化する前の「普通の文」のことを平文といいます.一方,これを情報を伝達したい相手以外の者が簡単に読むことのできないようにしたものを暗号文と言い,平文から暗号文を作成することを暗号化,その逆を復号化といいます.暗号化には何らかの暗号アルゴリズムが利用されますが,大抵の暗号アルゴリズムはそれ自体を理解しただけでは暗号を解読できず,実際に暗号文を復号化するためにはアルゴリズムとは別に鍵が必要になります.当然,暗号化する際にも鍵が必要です.シーザー暗号の場合,「何文字ずらすのか」という情報にあたる nが暗号化の鍵ということになります.

ところで,アルファベットは25文字しかないので, nを26以上の数字にしても n=0〜25のいずれかによる暗号化と同じ結果が得られます. nを0より小さい値にした場合も同様です.すなわち,平文の文字に相当する数字を P,暗号文の文字に相当する数字を Cと表すことにすれば,シーザー暗号による暗号化は次式のように一般化されることがわかります.

 \displaystyle\quad C \equiv P + n \pmod{26}

\Caesar命令の実装

さて,以下のような書式でシーザー暗号による暗号化を実現するマクロ\Caesarを考えます.

\Caesar{〈鍵〉}{〈平文〉}

第一引数に任意の整数を入れると,その整数 nを鍵として第二引数に渡した平文からシーザー暗号を生成します.

早速,実装してみましょう.なお,以下のコードはe-upTeX 3.14159265+pLaTeX2e(TeX Live 2014)での動作を確認していますが,ご利用は自己責任でお願いします.

\makeatletter

\newcount\@result

\def\calc@mod#1#2{%
  \bgroup
  \@tempcnta=#1\relax
  \@tempcntb=#2\relax
  \@whilenum\@tempcntb>\@tempcnta
    \do{\advance\@tempcntb-\@tempcnta}%
  \ifnum\@tempcntb=\@tempcnta
    \@tempcntb=0
  \fi
  \global\@result=\@tempcntb
  \egroup
}

\def\Caesar#1#2{%
  \bgroup
    % 鍵の処理
    \@tempcntb=#1\relax
    \ifnum\@tempcntb<0
      \multiply\@tempcntb\m@ne
      \calc@mod{26}{\@tempcntb}
      \@tempcntb=\@result
      \multiply\@tempcntb\m@ne
      \advance\@tempcntb26
    \else
      \calc@mod{26}{\@tempcntb}
      \@tempcntb=\@result
    \fi
    % 暗号化処理
    \@tfor\member:=#2\do{%
      \@tempcnta=\expandafter`\member\relax
      \ifnum\@tempcnta>`Z
        % 小文字
        \ifnum\@tempcnta<`a
          \member
        \else
          \ifnum\@tempcnta>`z
            \member
          \else
            \advance\@tempcnta\@tempcntb
            \ifnum\@tempcnta>`z
              \advance\@tempcnta-26
            \fi
            \char\@tempcnta
          \fi
        \fi
      \else
        % 大文字
        \ifnum\@tempcnta<`A
          \member
        \else
          \ifnum\@tempcnta>`Z
            \member
          \else
            \advance\@tempcnta\@tempcntb
            \ifnum\@tempcnta>`Z
              \advance\@tempcnta-26
            \fi
            \char\@tempcnta
          \fi
        \fi
      \fi
    }%
  \egroup
}

\makeatother

はじめに定義した\calc@mod命令は剰余を求めるための内部命令です.第二引数を第一引数で割った剰余を\@resultに代入して返します.暗号を扱う上でmodはよく出てくるものなので,他の暗号を実装するときにも利用できるよう独立に定義しておきました.なお,ここでは(見ての通り)第二引数が第一引数より小さくなるまで引き算をし続けるという泥臭い方法を用いていますが,今週のマクロ(第3週)の末尾で紹介されているようなテクニックを用いることも可能です.

続く「鍵の処理」ではさきほど定義した\calc@modを用いて第一引数に渡された鍵を0〜25までの値に変換しています.\calc@modは負の値に対応していないので,第一引数が負であった場合には一旦-1倍してから\calc@modを使用し,もう一度-1倍してから26を足すということを行っています(手抜き).

鍵を無事0〜25の値に収めたら,次はいよいよ暗号化を行ないます.全体としては\@tfor命令を用いて一文字ごとに変換を行っています.また,ここでは

`〈任意の文字〉

の形でアスキーコード(厳密にはTeXの内部コード)を表すことができることを活用しています.まず,アスキーコードの大小による条件分岐を多用してアルファベットの大文字・小文字を別々に補足し,それぞれ先ほどの「鍵の処理」で0〜25に収めた鍵(\@tempcntbに格納されている)を用いて\@tempcntaに格納されている平文文字のアスキーコードをずらします.ずらした結果,"Z"または"z"のアスキーコードよりも大きな値になってしまった場合は26を引き算しています.最後の\char命令は得られたアスキーコードを文字として出力させる命令です.なお,アルファベットではない文字(補足されなかった文字)はそのまま出力させています.

\Caesar命令の定義全体を\bgroup ... \egroupで囲っているのは,一時カウンター\@tempcnta\@tempcntbを操作した影響が外部に及ぶのを防ぐためです.

\Caesar命令を使ってみる

それでは,前節で定義した\Caesar命令を実際に使ってみましょう.

【入力】

\Caesar{3}{Hello,\ Brutus.\ It\ is\ nice\ weather!}

【出力】

Khoor, Euxwxv. Lw lv qlfh zhdwkhu!

このように,大文字は大文字,小文字は小文字で変換されて出力されます.,, ., !のようなアルファベット以外の文字はそのまま出力されています.一方,半角の空白は内部で用いている\@tfor命令が空白を無視する使用であるため,上の例のように明示的に示さない限り取り除かれてしまいます.むしろ,この性質はより強力な暗号文を作成する際に役立つかもしれません.

【入力】

\Caesar{17}{THIS IS A PEN.}

【出力】

KYZJZJRGVE.

また,シーザー暗号の場合は暗号化と復号化のアルゴリズムがまったく同一です.すなわち,\Caesar命令はシーザー暗号の復号化にもりようできます.

【入力】

\Caesar{-3}{Khoor,\ Euxwxv.\ Lw\ lv\ qlfh\ zhdwkhu!} \Caesar{9}{KYZJZJRGVE.}

【出力】

Hello, Brutus. It is nice weather! THISISAPEN.

シーザー暗号への攻撃

既にお気づきの通り,シーザー暗号は非常に弱い暗号です.鍵がたったの26通り *2 しかないため,虱潰しにより簡単に破ることができます.このような暗号解読法をブルートフォースアタックと呼びますが,シーザー暗号に対するブルートフォースアタックを行うための命令\BluteForceAtackを実装してみましょう.

\makeatletter

\def\BluteForceAtack#1{
  \begin{itemize}
  \@tempcnta=\z@
  \@whilenum\@tempcnta<26\do{%
    \item[key:\the\@tempcnta]\Caesar{\@tempcnta}{#1}
    \advance\@tempcnta\@ne}
  \end{itemize}
}

\makeatother

やってることは単純です.解読したい暗号文を引数として渡すと,\Caesarの第二引数として,鍵に0〜25までの数字を片っ端から突っ込んでいき,箇条書きにして表示させます.実際に使用してみましょう.

【入力】

\BluteForceAtack{QOB\ MCI\ FSOR\ HVWG?}

【出力】

\BluteForceAtackの出力

すると,key:12のときのみ"CAN YOU READ THIS?"という意味のある文字列が発見できるので,これが平文ということがわかります.

このように,シーザー暗号は極めて単純で原始的な暗号であり,解読も容易です.したがって,今回作成した\Caesar命令により情報の安全性は一向に確保されないので,これを重要な情報の保護には使用しないようにお願いたします.


2015/5/15 更新

\calc@modの仕様を変更しました.それにともない,コードや本文の一部を修正しました.

2015/5/18 更新

行末処理とコードの合理的でない部分を修正しました.

*1:そのため n=3の場合のみをシーザー暗号と呼称し,一般の nについてずらすものについてはシフト暗号と呼ぶ流儀もあるようです.本記事では, nを任意の整数とするものについてシーザー暗号と呼ぶことにします.

*2:うち1つは平文のままになるので,実質的には25通りです.