共通鍵暗号


SSL/TLSに関して調べていく中で、暗号化に関して興味が出てきたので
暗号化で使われている技術がどんなものかを記事でまとめていこうと思います。

この記事では共通鍵暗号に関して説明します。公開鍵暗号や一方向ハッシュ関数なども別の記事でまとめていく予定です。

# 共通鍵暗号とは

下記の画像のように、通信を行う二人が共通の鍵を使って、暗号化したデータの通信を行います。

![共通鍵通信.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/d637769a-ae1f-dbb9-b0ab-120468df6bd6.png)

通信者同士が持っている共通の鍵は暗号化と復号化に使えるので、送信者は鍵を使って暗号化してデータを送り、受信者は鍵を使って復号化してデータを確認します。

![共通鍵データ送信.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/37ae07ce-718d-453b-9ca8-c0c9aaefb749.png)

使う鍵は一種類で管理は楽ですが、あらかじめ鍵を相手に渡しておく必要があります。
近くにいる人へ鍵を渡すくらいなら直接渡せば問題ありませんが、インターネットでは不特定多数の人と通信したいので、鍵をどうやって送り届けるかの問題(鍵配送問題)が残ります。

# 共通鍵暗号方式

ここからは実際に使われている(使われたことのある)共通鍵暗号の方式を説明していきます。

# DES

64ビット(7ビットお行きにエラー検出用のビットが入るので、実質は56ビットの鍵を使って暗号化を行います。
64ビットの平文(暗号化の前のデータ)に対して暗号化が可能で、この暗号化できる64ビットの平文を「ブロック」と呼びます。このブロックごとに暗号化することを **「ブロック暗号」** と呼び、ブロック暗号の暗号化をくりかえす方法を **「モード」** と呼びます。
また、DESはブルートフォースアタックにより短時間で解読できるため、非推奨の暗号方式です。実際に使用することはないように気を付けてください。

# ファイステルネットワーク

ブロック暗号でよく使われる構造です。ラウンドと呼ばれる暗号化の1ステップを何度も繰り返すことを指します。
ラウンドは下記の手順で行います。

1. 暗号化するデータを入力データの左半分か右半分に分ける
2. 入力データの右半分を鍵(サブ鍵)とラウンド関数を用いて暗号化する
3. 暗号化したデータを、左半分とXORをとり、右半分のデータとする
4. 左半分と右半分を入れ替える

1〜4を1ラウンドと考えてこれを繰り返すことで暗号化します。サブ鍵は1ラウンドごとに異なる鍵が使用されます。
ラウンド関数は、暗号方式によって変わるため、ここでの説明は割愛します。
復号化の際は、サブ鍵を使って1ラウンドの処理を行うことで復号化します(XORの性質を利用します)。複数のラウンドによる暗号化でも、サブ鍵の使う順番を逆にしてラウンドの処理を行うことで復号化します。

# トリプルDES(3DES)

DESがブルートフォースアタックにより現実的な時間で解読可能となったので、この対策としてDESを三段重ねにすることで対策を行った暗号方式です。下記の順序で暗号化と復号化を繰り返します。

1. DES暗号化
1. DES復号化
1. DES暗号化

ここで、三つの異なる鍵を使う場合はDES-EDE3、二つの鍵を使う場合はDES-EDE2と呼ばれています。
処理スピードが速くないことから、推奨されることは少ないです。セキュリティの書籍には「当面の利用を認める」のような記述になっていることが多いです。

# AES

現状(2024/12)有効な攻撃方法が見つかっていないため、推奨されることが多い暗号方式です。トリプルDESより高速なため、トリプルDESよりAESが推奨されています。
SPN構造とブロック暗号モードを使用して、暗号化と復号化が行われます。SPN構造とブロック暗号モードについて説明していきます。

# SPN構造

複数のラウンドから構成されており、下記の四つの操作を続けて行うのを1ラウンドとする構造です。

1. SubBytes
	16バイトの入力に対して、あらかじめ用意していた換字表を基に、1バイトずつ変換
2. ShiftRows
	4バイト単位のまとまった行を左シフトしてまぜこぜにする
3. MixColumns
	4バイトの値をビット演算を用いて4バイトの値に変換する。例えば一列分を行列計算を使って変換
4. AddRoundKey
	MixColumnsの出力とラウンドキーをXORをとる


入力ブロックは128ビットで、16バイト(4*4の行列)ごとに演算を行います。
暗号化は１〜４の順に行い、復号化は４〜１の順に逆の操作をして行います。

# ブロック暗号モード

任意の長さの平文をブロック暗号で暗号化する際の方法のことを指します。モードにはいくつかありますので、それを説明していきます。
ブロックごとで順番に暗号化する(ECBモード)と、暗号化したデータがパターン化され、セキュリティホールとなってしまうので、モードの選択にも注意が必要です。

## ECBモード

平文ブロックを順番に暗号化します。最後の平文のブロックが暗号化に必要なブロックサイズ未満の際は、乱数をパディングして暗号化します。
暗号化後のデータでも平文の繰り返しが認識できてしまうので、非推奨とされています。

## CBCモード

一つ前の暗号文ブロックと平文ブロックのXORを取ってから暗号化します。先頭のブロックは一つ前のブロックがないので、初期化ベクトルを与えます。
SSL/TLSでも使用されており、下記の手順で暗号化します。

1. (一つ前の暗号化ブロック(or 初期化ベクトル)) ⊕ (平文ブロック)
2. １の結果を暗号化

![CBC.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/01dbbf01-5178-9c8c-a8fb-23ee1c8d5e86.png)


復号化の際は下記の手順となります。

1. 暗号ブロックを復号化
2. (1の結果) ⊕ (一つ前の暗号化ブロック)

初期化ベクトルは、攻撃に対する対策が必要なため、再現不可能性を満たす乱数で設定します。
最後の平文のブロックが暗号化に必要なブロックサイズ未満の際にパディングするデータも、攻撃に対する対策が必要です。再現不可能性を満たす乱数か、最後のブロックの一つ前の暗号文ブロックの末尾をパディングします。
最後のブロックの一つ前の暗号文ブロックの末尾をパディングすることは、CTSモードと呼ばれています。

## CFBモード

一つ前の暗号文ブロックを暗号化し、平文ブロックのXORを取ります。下記の手順で暗号化します。
1. 一つ前の暗号化ブロックを暗号化
2. (1の結果) ⊕ (平文ブロック)

![CFB.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/45281afc-6e66-4b07-30ca-c09da0e085f0.png)

復号化は下記の手順となります。
1. 一つ前の暗号化ブロックを暗号化
2. (1の結果) ⊕ (暗号化ブロック)


初期化ベクトルはCBCモードと同様、再現不可能性を満たす乱数で設定します。
平文を暗号化しないため、任意のブロックサイズが設定可能で、パディングの必要ないというメリットがあります。
ただし、再生攻撃(復号可能な暗号化データを不必要に何度も繰り返し送る攻撃)が可能なため、メッセージ認証コードによる対策が必要です。

## OFBモード

初期化ベクトルを何度も暗号化し、平文ブロックとXORを取ります。下記の手順で暗号化します。

1. 一つ前に使用した初期化ベクトルの暗号化データを再び暗号化
2. (1の結果) ⊕ (平文ブロック)

![OFB.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/3b0bc76e-e936-38c9-314f-311340a8bf14.png)


復号化は下記の手順となります。

1. 一つ前に使用した初期化ベクトルの暗号化データを再び暗号化
2. (1の結果) ⊕ (暗号化ブロック)

暗号化するのは初期化ベクトルのため、あらかじめ暗号化に使用する鍵(鍵ストリーム)を用意しておくことができる。並列処理ができる環境であれば高速処理が可能。

## CTRモード

1ずつ増加していくカウンタを暗号化して、平文とXORを取るモードです。カウンタの初期値は暗号化の度に異なる値（ノンス）をもとにして作ります。カウンタはノンス＋カウンタ値となるように設定します。例えば前半8byteはノンス、後半8byteはブロック番号(この値を暗号化のたびカウントアップしていく)、のような形で設定されます。

下記の手順で暗号化します。
1. 一つ前に使用したカウンタをカウントアップして暗号化
2. (1の結果) ⊕ (平文ブロック)

![CTR.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266944/e5dcddc9-8fd6-40d3-a3cf-3ffc939fd7a5.png)


復号化は下記の手順です。
1. 一つ前に使用したカウンタをカウントアップして暗号化
2. (1の結果) ⊕ (暗号化ブロック)

パディングが必要なく、あらかじめ暗号化に使用する鍵(鍵ストリーム)を用意しておくことができるため、並列処理ができる環境であれば高速処理できるのがメリットです。

# 参考文献

- 暗号技術入門 秘密の国のアリス(書籍)
https://cr.hyuki.net/