C++で実装するPlayfair暗号 ― 暗号化・復号の仕組みとサンプルプログラム
この方式では、単純な単一文字置換暗号のように1文字ずつではなく、2文字ずつのペア(ダイグラフ)単位で暗号化を行います。
Playfair暗号では、まず「鍵表(キーテーブル)」を作成します。鍵表は5×5のアルファベット格子で、平文を暗号化する際の鍵として機能します。必要なのは26文字ではなく25文字であるため、アルファベットから1文字(通常はJ)を除外し、残りの25文字をすべて重複なく配置します。平文にJが含まれていた場合は、Iに置き換えて処理します。
送信者と受信者は、あらかじめ共通の鍵(ここでは「tutorials」)を取り決めておきます。鍵表には、まず鍵フレーズの文字を重複を除いて左から右へ順に配置し、残りのマスには未使用のアルファベットを自然な順序で埋めていきます。完成した鍵表は以下のようになります。

Playfair暗号の処理手順
まず、平文のメッセージを2文字ずつのペア(ダイグラフ)に分割します。文字数が奇数になる場合は、末尾にZを追加します。例として、「hide money」というメッセージを暗号化する場合、次のように書きます。
HI DE MO NE YZ
暗号化のルールは以下の3つです。
2つの文字が同じ列にある場合:各文字の下にある文字を取ります(最下段の場合は最上段に戻る)。例:「H」と「I」は同じ列にあるため、それぞれの下の文字に置き換えます。HI → QC

2つの文字が同じ行にある場合:各文字の右にある文字を取ります(最右端の場合は最左端に戻る)。例:「D」と「E」は同じ行にあるため、それぞれの右の文字に置き換えます。DE → EF

どちらのルールにも当てはまらない場合:2つの文字を対角とする長方形を考え、その水平方向の反対側の角にある文字を取ります。

これらのルールを適用すると、「hide money」を鍵「tutorials」で暗号化した結果は次のようになります。
QC EF NU MF ZV
Playfair暗号の復号は、同じ処理を逆方向に行うだけでよいため非常に簡単です。受信者が同じ鍵を持っていれば同一の鍵表を作成でき、その鍵で暗号化されたメッセージを復号できます。
以下に、Playfair暗号でメッセージを暗号化・復号するC++プログラムを示します。
アルゴリズム
Begin
Function void play( int dir )
For it = msg.begin() to it != msg.end()
If ( getPos( *it++, j, k ) )
If ( getPos( *it, p, q) )
If ( j == p )
nmsg+= getChar( j, k + dir )
nmsg += getChar( p, q + dir )
else if( k == q )
nmsg += getChar( j + dir, k )
nmsg += getChar( p + dir, q )
else
nmsg += getChar( p, k )
nmsg += getChar( j, q )
done
done
done
msg = nmsg
done
End
サンプルコード
#include <iostream>
#include <string>
using namespace std;
class playfair {
public:
string msg; char n[5][5];
void play( string k, string t, bool m, bool e ) {
createEncoder( k, m );
getText( t, m, e );
if( e )
play( 1 );
else
play( -1 );
print();
}
private:
void play( int dir ) {
int j,k,p,q;
string nmsg;
for( string::const_iterator it = msg.begin(); it != msg.end(); it++ ) {
if( getPos( *it++, j, k ) )
if( getPos( *it, p, q) ) {
//for same row
if( j == p ) {
nmsg+= getChar( j, k + dir );
nmsg += getChar( p, q + dir );
}
//for same column
else if( k == q ) {
nmsg += getChar( j + dir, k );
nmsg += getChar( p + dir, q );
} else {
nmsg += getChar( p, k );
nmsg += getChar( j, q );
}
}
}
msg = nmsg;
}
void print() //print the solution {
cout << "\n\n Solution:" << endl;
string::iterator it = msg.begin(); int count = 0;
while( it != msg.end() ) {
cout << *it;
it++;
cout << *it << " ";
it++;
if( ++count >= 26 )
cout << endl;
count = 0;
}
cout << endl << endl;
}
char getChar( int a, int b ) { //get the characters
return n[ (b + 5) % 5 ][ (a + 5) % 5 ];
}
bool getPos( char l, int &c, int &d ) { //get the position
for( int y = 0; y < 5; y++ )
for( int x = 0; x < 5; x++ )
if( n[y][x] == l ) {
c = x;
d= y;
return true;
}
return false;
}
void getText( string t, bool m, bool e ) { //get the original message
for( string::iterator it = t.begin(); it != t.end(); it++ ) {
//to choose J = I or no Q in the alphabet.
*it = toupper( *it );
if( *it < 65 || *it > 90 )
continue;
if( *it == 'J' && m )
*it = 'I';
else if( *it == 'Q' && !m )
continue;
msg += *it;
} if( e ) {
string nmsg = ""; size_t len = msg.length();
for( size_t x = 0; x < len; x += 2 ) {
nmsg += msg[x];
if( x + 1 < len ) {
if( msg[x] == msg[x + 1] ) nmsg += 'X';
nmsg += msg[x + 1];
}
}
msg = nmsg;
}
if( msg.length() & 1 )
msg += 'X';
}
void createEncoder( string key, bool m ) { //creation of the key table
if( key.length() < 1 )
key= "KEYWORD";
key += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string s= "";
for( string::iterator it = key.begin(); it != key.end(); it++ ) {
*it = toupper( *it );
if( *it < 65 || *it > 90 )
continue;
if( ( *it == 'J' && m ) || ( *it == 'Q' && !m ) )
continue;
if( s.find( *it ) == -1 )
s += *it;
}
copy( s.begin(), s.end(), &n[0][0] );
}
};
int main( int argc, char* argv[] ) {
string k, i, msg;
bool m, c;
cout << "Encrpty or Decypt? ";
getline( cin, i );
c = ( i[0] == 'e' || i[0] == 'E' );
cout << "Enter a key: ";
getline( cin, k);
cout << "I <-> J (Y/N): ";
getline( cin, i );
m = ( i[0] == 'y' || i[0] == 'Y' );
cout << "Enter the message: ";
getline( cin, msg );
playfair pf;
pf.play( k, msg,m, c );
return system( "pause" );
}
出力結果
Encrpty or Decypt? d Enter a key: players I <-> J (Y/N): y Enter the message: OK GC GC MZ MQ CF YA RL QH OM Solution: TH IS IS TU TO RI AL SP OI NT
-
接続行列を使ってグラフを表現するC++プログラムの解説
接続行列(インシデンス行列)とはグラフの接続行列(インシデンス行列)は、グラフをメモリ上に格納するためのもうひとつの表現方法です。隣接行列と異なり、接続行列は正方行列ではありません。そのサイズは V × E で表されます。ここで V はグラフの頂点数、E は辺の数です。この行列では、各行に頂点が配置され、各列に辺が配置されます。ある辺 e {u, v} に対しては、列 e のうち頂点 u と頂点 v に対応する位置に「1」がマークされます。これにより、「どの頂点がどの辺に接続しているか」という情報を直感的に把握できます。接続行列の計算量とメモリ使用量接続行列による表現では、構築時に O(V ×
-
隣接行列を使ってグラフを表現するC++プログラムの解説
グラフの隣接行列(Adjacency Matrix)とは、サイズが V × V の正方行列のことです。ここでの V はグラフ G の頂点数を表します。行列の行と列にはそれぞれ頂点が対応しており、頂点 i から頂点 j への辺が存在する場合は、i 行 j 列の要素に「1」(重み付きグラフの場合は非ゼロの値)を格納します。辺が存在しない場合は、その位置には「0」が入ります。 隣接行列表現の計算量 隣接行列は計算時に O(V2) の記憶領域を必要とします。グラフが最大数の辺を持つ場合でも最小数の辺しか持たない場合でも、必要なメモリ量は同じです。つまり、辺の数に依存せず常に V × V 分の領域を確