CBCモードを使ってファイルの暗号化を行ってみた

##ブロック暗号とは
ある特定のビット数のまとまりを一度に処理する暗号アルゴリズムの総称。

##CBCモードとは
CBCモードとはCiper Block Chainingモードの略で1つ前の暗号ブロックと平文ブロックのXORを取ってから暗号化を行います。
CBCモードは以下の手順で暗号化します。
![暗号手順.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233394/4f84edab-6c2d-37f6-f44d-ba4eae4b69e7.png)


実際にCBCモード暗号を行うソースを書いてみます。
今回は暗号化にはXOR暗号を用いて、ブロック長を8ビットとしてCBCモードを行います。
ソースは以下になります。

```C++:メインプロセス
#include <string>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <fstream>
#define Block 1

using namespace std;
void cipher(char* dst);
int main()
{
	string fileName;	//ファイル名

	//ファイル名からバイナリファイルで読み込む
	std::cout << "暗号化するファイル名を入力してください\n";
	//キーボード入力からファイル名を取得する
	getline(cin, fileName);
	std::ifstream ifs(fileName, std::ios::binary);

	string outFileName;	//ファイル名
	//ofstreamを読み取りモードで開き、末尾に移動
	std::cout << "出力するファイル名を入力してください\n";
	//キーボード入力からファイル名を取得する
	getline(cin, outFileName);
	std::ofstream ofs(outFileName, std::ios::app | std::ios::binary);

	//読み込みデータ
	char data[Block];

	//初期化ベクトル
	char initialData[Block];
	memset(initialData, 'I', Block);

	//1つ前の暗号ブロック
	char cipherBlockPre[Block];

	//暗号ブロック
	char cipherBlock[Block];

	//データ読込
	ifs.read(data, Block);
	//ブロック長ごとに処理
	for (int i = 0; i < Block; i++)
	{
		cipherBlock[i] = data[i] ^ initialData[i];
	}
	//暗号化
	cipher(cipherBlock);
	//暗号化したブロックを出力
	ofs.write(cipherBlock, Block);
	//1つ前の暗号ブロックに暗号化したブロックを格納
	memcpy(cipherBlockPre, cipherBlock, Block);
	do{
		//データ読込
		ifs.read(data, Block);
		//データがなかった場合終了する。
		if (ifs.eof()) break;
		//ブロック長ごとに処理
		for (int i = 0; i < Block; i++)
		{
			cipherBlock[i] = data[i] ^ cipherBlockPre[i];
		}
		//暗号化
		cipher(cipherBlock);
		//暗号化したブロックを出力
		ofs.write(cipherBlock, Block);
		//1つ前の暗号ブロックに暗号化したブロックを格納
		memcpy(cipherBlockPre, cipherBlock, Block);
	}while (true);
	
}
```
```C++:暗号化プロセス
void cipher(char* dst)
{
	//暗号鍵
	char cipherBlockTemp[Block];
	memset(cipherBlockTemp, 'S', Block);
	//ブロック長ごとに処理
	for (int i = 0; i < Block; i++)
	{
		//XOR暗号
		dst[i] = dst[i] ^ cipherBlockTemp[i];
	}
	return;
}
```

作成したプログラムで以下の文章を暗号化してみます。
![暗号前.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233394/6b07372c-ca94-4136-0277-b5933ce359e6.png)

暗号化すると以下のようになります。
![暗号化.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233394/17679797-33b4-8528-3998-c6ae9c9f0f2b.png)

元の文章が全くなく、この文章から元の文章を推測するのは難しいです。

##復号化とは
暗号文を平文に変換することを指します。
暗号化した文章を暗号した文章と逆の手順で復号化します。
復号化は以下のように行います。
CBCモードは以下の手順で復号化します。
![復号手順.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233394/93a6e534-4585-60b7-0f3c-8b6e91c32a51.png)


実際にCBCモード暗号を復号するソースを書いてみます。
ソースは以下になります。

```C++:メインプロセス
#include <iostream>
#include <string>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <fstream>
#define Block 1

using namespace std;
void decode(char* dst);
int main()
{

	string fileName;	//ファイル名


	//ファイル名からバイナリファイルで読み込む
	std::cout << "復号化するファイル名を入力してください\n";
	//キーボード入力からファイル名を取得する
	getline(cin, fileName);
	std::ifstream ifs(fileName, std::ios::binary);

	string outFileName;	//ファイル名
	//ofstreamを読み取りモードで開き、末尾に移動
	std::cout << "出力するファイル名を入力してください\n";
	//キーボード入力からファイル名を取得する
	getline(cin, outFileName);
	std::ofstream ofs(outFileName, std::ios::app | std::ios::binary);

	//読み込みデータ
	char data[Block];

	//初期化ベクトル
	char initialData[Block];
	memset(initialData, 'I', Block);

	//一時保存読み込みデータ
	char dataTemp[Block];

	//1つ前の暗号ブロック
	char cipherBlockPre[Block];

	//復号ブロック
	char decodeBlock[Block];

	//データ読込
	ifs.read(data, Block);
	//1つ前の暗号ブロックに暗号化されているブロックを格納
	memcpy(cipherBlockPre, data, Block);
	//復号化
	decode(data);
	//ブロック長ごとに処理
	for (int i = 0; i < Block; i++)
	{
		decodeBlock[i] = data[i] ^ initialData[i];
	}
	//暗号化したブロックを出力
	ofs.write(decodeBlock, Block);
	do {
		//データ読込
		ifs.read(data, Block);
		//データがなかった場合終了する。
		memcpy(dataTemp, data, Block);
		//復号化
		decode(data);
		if (ifs.eof()) break;
		//ブロック長ごとに処理
		for (int i = 0; i < Block; i++)
		{
			decodeBlock[i] = data[i] ^ cipherBlockPre[i];
		}
		//暗号化したブロックを出力
		ofs.write(decodeBlock, Block);
		//1つ前の暗号ブロックに暗号化されているブロックを格納
		memcpy(cipherBlockPre, dataTemp, Block);
	} while (true);
	//

}
```
```C++:復号化プロセス
//復号化
void decode(char* dst)
{
	//暗号鍵
	char cipherBlockTemp[Block];
	memset(cipherBlockTemp, 'S', Block);
	//ブロック長ごとに処理
	for (int i = 0; i < Block; i++)
	{
		//XOR暗号
		dst[i] = dst[i] ^ cipherBlockTemp[i];
	}
	return;
}
```
作成したプログラムで暗号化された文章を復号化してみます。
![復号化.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233394/a9ae4a07-3205-bf98-97cc-e35da37fac79.png)

暗号文を平文に変換することができました。

##おわりに

大事なファイルは独自で暗号化してみるのもいいかもしれません。
今回のプログラムは平文ブロックがブロック長に満たない場合の考慮が出来ていなかったため、
今度はパディングも考慮して、作ってみようと思います。