暗号生成・解読プログラムをC/C++/Java/Pythonで書く(ポリュビオスの暗号表編)
どうも、2年になって数Aで微分やりはじめてテンションが上がってるポテト君です。
前回は単一換字式暗号でした。
ちなみに春休み明けてから数週間経ったけど春休みの運動不足で未だに登校で筋肉痛です。
そんなことはどうでもいいんですよ。
ー 暗号表って…なんですか?!?!?!?!
1 2 3 4 5
1 A B C D E
2 F G H I,J K
3 L M N O P
4 Q R S T U
5 V W X Y Z
はい、これです。
アルファベットを 行数 列数 の順に並べれば暗号の完成です。
つまり"abc"は"111213"になります。
ー ポテト先生!!! IとJの変換結果が同じじゃないですか!どうするんですか!!
それは仕様です。
(訳:6列とか6行とかに拡張したりすることもあるそうです)
ー 5*5の二次元配列が使えそうですね!!!
使いません。
(今回は文字コードを使います。
そもそもインデックスだと1から始まるのが違和感なので(((((( )
追記:複合化部分をデバッグしました。
C言語
/**
* @brief ポリュビオスの暗号表(5*5)を扱います。
* 5*5の方式ではjとiの暗号が同じ結果になります。そのため、復号結果ではiはiのままjをiとします。
*
* @param mode "m":暗号化モード,"r":復号化モード
* @param sentence 変換する文字列(内容が変更される可能性があります。)
* @param sen_size sentenceの要素数
* @param return_array 変換後の文字列を格納する配列
*/
void polybius_square(char mode,char* sentence,int sen_size,char* return_array){
for (int i = 0; i < sen_size; i++)
sentence[i] = tolower(sentence[i]);
if(mode=='m'){
for(int i=0;i<sen_size;i++){
if(sentence[i]<'a' || sentence[i]>'z'){
for(int i=i;i<sen_size-1;i++){
sentence[i]=sentence[i+1];
}
sen_size--;
}
}
for(int i=0;i<sen_size;i++){
int c=sentence[i]-'a';
if(c<('j'-'a')){
return_array[2*i]='0'+(c/5+1);
return_array[2*i+1]='0'+(c%5+1);
}else{
c--;
return_array[2*i]='0'+(c/5+1);
return_array[2*i+1]='0'+(c%5+1);
}
}
return_array[sen_size*2]='\0';
}
if(mode=='r'){
for(int i=0;i<sen_size;i++){
if(sentence[i]<'1' || sentence[i]>'5'){
for(int i=i;i<sen_size-1;i++){
sentence[i]=sentence[i+1];
}
sen_size--;
}
}
for(int i=0;i<sen_size/2;i++){
return_array[i]='a'+(5*((int)sentence[2*i]-(int)'0'-1)+((int)sentence[2*i+1]-(int)'0'-1));
if(return_array[i]>='j'){
return_array[i]+=1;
}
}
return_array[sen_size/2]='\0';
}
}
大文字を小文字化する処理を一応最初につけときます。
今回の暗号は変換後と変換前の文字数が異なるため、対応してない文字を変換せずに結果に格納するというのはわかりずらいので暗号表の範囲外の文字は消すことにしました。
if文で暗号化/複合化モードに分かれた時の最初のfor文がそれです。
その次のfor文で変換を行います。
そのときに'j'の文字を境目に変換する値に変更を加えます。
変換する文字の'a'からのカウントcを、'j'-'a'と比べてインクリメント(複合化の場合)またはデクリメント(暗号化の場合)します。
この時比べるのが'j'でなく'j'-'a'なのに注意してください。
前者はjのアスキーコードで、後者はaを0としたjのカウントです。
暗号化では
行数は(c/5+1)
列数は(c%5+1)
で、数値は1~5の範囲のみなので'0'を足すことでその数字を表す文字に変換できます。
複合化では、その行数と列数から元のcを求めます。
cは5*(行数-1)+(列数-1)で求められ、
(5*((int)sentence[2*i]-(int)'0'-1)+((int)sentence[2*i+1]-(int)'0'-1))
となります。
cは'a'を0としたカウントなので、'a'を足しなおせば元の文字コードに戻るはずです。
今回は変換後の値を返すための配列を別で用意しているため、念のため最後のヌル文字('\0')を追加する処理も書いておきました。
C++
class Cipher{
/**
* @brief ポリュビオスの暗号表(5*5)を扱います。
* 5*5の方式ではjとiの暗号が同じ結果になります。そのため、復号結果ではiはiのままjをiとします。
*
* @param mode "m":暗号化モード,"r":復号化モード
* @param sentence 変換する文字列
* @return string 暗号文または平文
*/
public:static string polybius_square(string mode,string sentence){
string ret="",sent_t="";
for(int i=0;i<sentence.length();i++)sentence[i]=tolower(sentence[i]);
if(mode[0]=='m'){
for(int i=0;i<sentence.length();i++){
if(sentence[i]>='a' && sentence[i]<='z')sent_t+=sentence[i];
}
sentence=sent_t;
for(int i=0;i<sentence.length();i++){
int c=sentence[i]-'a';
if(c<('j'-'a')){
ret+=to_string(c/5+1);
ret+=to_string(c%5+1);
}else{
c--;
ret+=to_string(c/5+1);
ret+=to_string(c%5+1);
}
}
}
if(mode[0]=='r'){
for(int i=0;i<sentence.length();i++){
if(sentence[i]>='1' && sentence[i]<='5')sent_t+=sentence[i];
}
sentence=sent_t;
for(int i=0;i<sentence.length()/2;i++){
ret+='a'+(5*((int)sentence[2*i]-(int)'0'-1)+((int)sentence[2*i+1]-(int)'0'-1));
if(ret[i]>='j'){
ret[i]+=1;
}
}
}
return ret;
}
};
相変わらずCとC++は似ていますね。
String型を使用している以外は大した変化はないですね。
暗号表外の文字を消す処理は、暗号表内の文字だけを取り出した文字列に置き換えるという処理にしました。
Java
public class Cipher {
/**
* ポリュビオスの暗号表(5*5)を扱います。
* 5*5の方式ではjとiの暗号が同じ結果になります。そのため、復号結果ではiはiのままjをiとします。
*
* @param mode "m":暗号化モード,"r":復号化モード
* @param sentence 変換する文字列
* @return String 暗号文または平文
*/
public static String polybius_square(String mode,String sentence){
sentence = sentence.toLowerCase();
String ret="";
List<Integer> list_sen = sentence.chars().boxed().collect(Collectors.toList());
int a='a',j='j'-'a',z='z',c1='1',c5='5';
if(mode=="m"){
for(int i=0;i<list_sen.size();i++){
if(list_sen.get(i)<a || list_sen.get(i)>z){
list_sen.remove(i);
i--;
}
}
for(int i=0;i<sentence.length();i++){
int c=list_sen.get(i)-a;
if(c>=j)c--;
ret+=Integer.toString(c/5+1);
ret+=Integer.toString(c%5+1);
}
}
if(mode=="r"){
for(int i=0;i<list_sen.size();i++){
if(list_sen.get(i)<c1 || list_sen.get(i)>c5){
list_sen.remove(i);
i--;
}
}
for(int i=0;i<sentence.length()/2;i++){
int c=a+(5*(list_sen.get(2*i)-(int)'0'-1)+(list_sen.get(2*i+1)-(int)'0'-1));
if(c>=j+a)c++;
ret+=Character.toString(c);
}
}
return ret;
}
}
今までの記事でも同じようにC++と対して差がないです。
いつものごとく文字列を文字コードに変換したリストを作る処理が追加されています。下に示すやーつです。
List<Integer> list_sen = sentence.chars().boxed().collect(Collectors.toList());
Python
class Cipher:
def polybius_square(mode:str,sentence:str)->str:
"""
ポリュビオスの暗号表(5*5)を扱います。
5*5の方式ではjとiの暗号が同じ結果になります。そのため、復号結果ではiはiのままjをiとします。
mode "m":暗号化モード,"r":復号化モード
sentence 変換する文字列
->return 暗号文または平文
"""
sentence=sentence.lower()
ret=""
sent_t=""
a=ord('a')
j=ord('j')-ord('a')
if(mode=="m"):
for c in sentence:
if(ord(c)>=ord('a') and ord(c)<=ord('z')):
sent_t+=c
sentence=sent_t
for i in range(0,len(sentence)):
c=ord(sentence[i])-a
if(c>=j):c-=1
ret+=str(int(c/5+1))
ret+=str(int(c%5+1))
if(mode=="r"):
for c in sentence:
if(ord(c)>=ord('1') and ord(c)<=ord('5')):
sent_t+=c
sentence=sent_t
for i in range(0,int(len(sentence)/2)):
c=a+(5*(ord(sentence[2*i])-ord('0')-1)+(ord(sentence[2*i+1])-ord('0')-1))
if(c>=j+a):c+=1
ret+=chr(c)
return ret
特筆すべきところは…ないですかね
ということで完成いたしました。
このシリーズ、自分のプログラムの潜在バグを見つけられるので良き(((
次回はスキュタレー暗号編となります。
ところで…これでもう作り置き(?)のプログラムは終わっちゃったんですよね。
こんな暗号化方式のプログラムが見たいとかあったらコメントください。
してくれたら…
僕が喜びます()
この記事が気に入ったらサポートをしてみませんか?