暗号生成・解読プログラムをC/C++/Java/Pythonで書く(スキュタレー暗号・まとめ編)

どうも、GWでゴールデンなポテトになってるポテト君です。

前回はポリュビオスの暗号表編でした。

今回はゴールデンなスキュタレー暗号を作っていきます。

ー ゴールデンなスキュタレー暗号ってなんですか???

ただのスキュタレー暗号です。

ということでスキュタレー暗号のアルゴリズムから紹介していきます。

スキュタレー暗号というのは、置換式暗号の1つです。
要するに文字を置き換えて暗号化します。

5列のテーブルに横から文字を代入していき、縦読みするという作業です。
縦読みでメッセージ伝えてくるってのはよくありますよね。

ex)
this is test

   0  1  2  3  4
0  t  h  i  s  
1  i  s      t   e
2  s  t

tishsti st e

C言語

#define SCYTALE_LENGTH 500
/**
 * @brief スキュタレー暗号を扱います。
 * 5列に分けて暗号化・復号化を行います。
 * sen_sizeは文字数にしてください。
 * return_arrayは少し余裕をもって宣言しておいたほうがいいです。
 * 言語の仕様上変数の値を配列の要素数の宣言に使用できなかったので、500個(499文字)までに対応させました。
 * Chipher.hのマクロSCYTALE_LENGTHを書き換えることによって最大文字数を変更できます。
 * SCYTALE_LENGTHは5の倍数にしてください。
 * @param mode "m":暗号化モード,"r":復号化モード
 * @param sentence 変換する文字列
 * @param sen_size sentenceの文字数
 * @param return_array 変換後の文字列を格納する配列
*/
void scytale(char mode,char* sentence,int sen_size,char* return_array){
    if(mode=='m'){
        int table[5][(int)(SCYTALE_LENGTH)/5];
        for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
        for(int i=0;i<sen_size;i++){
            table[i%5][i/5]=sentence[i];
        }
        int k=0;
        for(int i=0;i<5;i++){
            for(int j=0;table[i][j]!=0;j++){
                if(j>=(int)(SCYTALE_LENGTH)/5)break;
                return_array[k]=table[i][j];
                k++;
            }
        }
        return_array[k]='\0';
    }
    if(mode=='r'){
        int table[5][(int)(SCYTALE_LENGTH)/5];
        for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
        for(int i=0;i<sen_size;i++){
            table[i%5][i/5]=sentence[i];
        }
        int k=0;
        for(int i=0;i<5;i++){
            for(int j=0;table[i][j]!=0;j++){
                if(j>=(int)(SCYTALE_LENGTH)/5)break;
                if(k<sen_size)table[i][j]=sentence[k];
                k++;
            }
        }
        for(k=0;k<sen_size;k++){
            return_array[k]=table[k%5][k/5];
        }
        return_array[k]='\0';
    }
}

要素を並べるテーブルを作るときの問題として、C言語ではリストは標準で入ってないのでリスト化は検討課題としておいて、配列で代用します。
つまり要素数に限りが出てきます。
C言語では配列の宣言に変数を使用できませんが、少しでも拡張性を持たせるためにマクロ定義を使います。
マクロ定義とは以下のようなものです。

#define HOGE 42

コンパイル時にHOGEが42に置き換えられます。

テーブル作って代入せずに直接インデックスを指定できないかって話ですが、多分できそうですけど今回はそうしてないのでおいときます。

マクロ定義を配列長として使うときに(int)でキャストしてますがそれしないとコンパイラに怒られるとかそんなんだったと思います。

テーブルは0で初期化しておいて文字を代入します。投げるときに文字列の長さがテーブルぴったりかなんて気にしないのでテーブルの0が入ってる部分は文字が存在しないものとします。
ASCIIにおいて0はnoneを指すのでこれで問題ないです。

今回は渡された文字列を直接変更せずreturn_arrayで返してますがこれは気まぐれというかポリュビオスの暗号表の引数リストをコピペしただけですね。揃えといた方がよかったかも。

kはreturn_array上でアクセス中のインデックスを代入します。
入り子のfor文において、iを列、jを行として、iは5で回数指定していますが、jは文字が存在するか(0かどうか)で判断しています。
この場合jがtableのインデックスを超える可能性があるのでbreak文を挿入しておきます。

一応最後にヌル文字を代入しておきます。

複合化に関してはテーブルに縦置きして横に読み直せばできます。

C++

#define SCYTALE_LENGTH 500
class Cipher{
    /**
     * @brief スキュタレー暗号を扱います。
     * 5列に分けて暗号化します。
     * 言語の仕様上変数の値を配列の要素数の宣言に使用できなかったので、500個(499文字)までに対応させました。
     * Chipher.hppのマクロSCYTALE_LENGTHを書き換えることによって最大文字数を変更できます。
     * SCYTALE_LENGTHは5の倍数にしてください。
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @return string 暗号文または平文
     */
    public:static string scytale(string mode,string sentence){
        string ret="";
        int sen_size=sentence.length();
        if(mode[0]=='m'){
            int table[5][int(SCYTALE_LENGTH)/5];
            for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
            for(int i=0;i<sen_size;i++){
                table[i%5][i/5]=sentence[i];
            }
            for(int i=0;i<5;i++){
                for(int j=0;table[i][j]!=0;j++){
                    if(j>=int(SCYTALE_LENGTH)/5)break;
                    ret+=table[i][j];
                }
            }
        }
        if(mode[0]=='r'){
            int table[5][int(SCYTALE_LENGTH)/5];
            for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
            for(int i=0;i<sen_size;i++){
                table[i%5][i/5]=sentence[i];
            }
            int k=0;
            for(int i=0;i<5;i++){
                for(int j=0;table[i][j]!=0;j++){
                    if(j>=int(SCYTALE_LENGTH)/5)break;
                    if(k<sentence.length())table[i][j]=sentence[k];
                    k++;
                }
            }
            for(int i=0;i<sen_size;i++){
                ret+=table[i%5][i/5];
            }
        }
        return ret;
    }
};

C++だったら標準でリストぐらい用意されてそうですがC言語の方式に従います。

毎度のことですがCとC++のプログラムはstring型ぐらいしか変化してないのでここの説明はあんま重要じゃないです。

Java

class Cipher{
    /**
     * スキュタレー暗号を扱います。
     * 5行に分けて暗号化します。
     *
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @return String 暗号文または平文
     */
    public static String scytale(String mode,String sentence){
        String ret="";
        List<Integer> list_sen = sentence.chars().boxed().collect(Collectors.toList());
        int table[][]=new int[5][(sentence.length()+4)/5];
        for(int i=0;i<(sentence.length()+4)/5*5;i++)table[i%5][i/5]=0;
        for(int i=0;i<sentence.length();i++){
            table[i%5][i/5]=list_sen.get(i);
        }
        if(mode=="m"){
            for(int i=0;i<5;i++){
                for(int j=0;j<(sentence.length()+4)/5;j++){
                    if(table[i][j]!=0)ret+=Character.toString(table[i][j]);
                }
            }
        }
        if(mode=="r"){
            int k=0;
            for(int i=0;i<5;i++){
                for(int j=0;j<(sentence.length()+4)/5;j++){
                    if(k<sentence.length() && table[i][j]!=0)table[i][j]=list_sen.get(k);
                    k++;
                }
            }
            for(int i=0;i<sentence.length();i++){
                ret+=Character.toString(table[i%5][i/5]);
            }
        }
        return ret;
    }
}

Javaに関しては配列の要素数を初期化時に動的に決定できるので行数は(sentence.length()+4)/5にします。
int型同士の演算では切り捨てなので1行足りないとかならないよう+4してから5割ります。

list_senの初期化はsentenceを文字コードリスト化する処理です。

暗号化でも複合化でも両方行う処理はif文から出しておきました。

入り子のfor文はjを配列長で制御できるようになりました。
文字が存在するかどうかの確認のif文を忘れないようにします。

Python

class Cipher:
    def scytale(mode:str,sentence:str)->str:
        """
        スキュタレー暗号を扱います。
        5列に分けて暗号化します。
        mode "m":暗号化モード,"r":復号化モード
        sentence 変換する文字列
        ->return 暗号文または平文
        """
        ret=""
        if(mode=="m"):
            table=[[0,0,0,0,0]]
            for i in range(0,len(sentence)):
                if(len(table)<=int((i)/5)):table.append([0,0,0,0,0])
                table[int(i/5)][i%5]=ord(sentence[i])
            for i in range(0,5):
                for j in range(0,len(table)):
                    if(table[j][i]!=0):ret+=chr(table[j][i])
        if(mode=="r"):
            table=[[0,0,0,0,0]]
            for i in range(0,len(sentence)):
                if(len(table)<=int((i)/5)):table.append([0,0,0,0,0])
                table[int(i/5)][i%5]=ord(sentence[i])
            k=0
            for i in range(0,5):
                for j in range(0,len(table)):
                    if(k<len(sentence) and table[j][i]!=0):
                        table[j][i]=ord(sentence[k])
                        k+=1
            for i in range(0,len(sentence)):
                if(table[int(i/5)][i%5]!=0):ret+=chr(table[int(i/5)][i%5])
        return ret

Pythonは標準がリストで、配列を扱う方が逆にムズイのでリストで処理します。

リストの初期化に関して、2次元リストで2d_list=[[0]*x]*yみたいなことしたら2d_list内のリストがすべて同じアドレスのリストを持ってしまったってトラウマがあったので[0,0,0,0,0]としていますが、この場合は[0]*5で大丈夫です。

tableに値を代入する部分では、tableの長さが足りなくなったら新しいリストを追加するようにします。


ということで、今のところ書いた暗号プログラムは全部出したので過去のものも合わせてプログラム全文を貼っておきます

プログラム全文

C言語

/**
 * @file Cipher.h
 * @author PotatoTimeKun (https://github.com/PotatoTimeKun)
 * @brief 暗号を扱う関数が入っています。
 * @details 関数一覧
 * void ceaser(char mode, char *sentence, int sen_size, int shift)
 * void vegenere(char mode, char *sentence, int sen_size, char *key, int key_size)
 * void substitution(char mode, char *sentence, int sen_size, char *key)
 */
#ifndef CHIPHER_H_INCLUDE
#define CHIPHER_H_INCLUDE
#define SCYTALE_LENGTH 500
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/**
 * シーザー暗号を扱います。
 * @param mode "m":暗号化モード,"r":復号化モード
 * @param sentence 変換する文字列(かつ変換後の文字列を格納する配列)
 * @param sen_size sentenceの要素数
 * @param shift シフトする数
 */
void ceaser(char mode, char *sentence, int sen_size, int shift)
{
    for (int i = 0; i < sen_size; i++)
        sentence[i] = tolower(sentence[i]);
    if (mode == 'm')
    {
        if (shift < 0)
        {
            ceaser('r', sentence, sen_size, -shift);
            return;
        }
        for (int i = 0; i < sen_size; i++)
        {
            if (sentence[i] >= 'a' && sentence[i] <= 'z')
            {
                sentence[i] = 'A' + (sentence[i] - 'a' + shift) % 26;
            }
        }
    }
    if (mode == 'r')
    {
        if (shift < 0)
        {
            ceaser('m', sentence, sen_size, -shift);
            return;
        }
        for (int i = 0; i < sen_size; i++)
        {
            if (sentence[i] >= 'a' && sentence[i] <= 'z')
            {
                int p = (sentence[i] - 'a' - shift) % 26;
                if (p < 0)
                    p += 26;
                sentence[i] = 'a' + p;
            }
        }
    }
}
/**
 * ヴィジュネル暗号を扱います。
 * @param mode "m":暗号化モード,"r":復号化モード
 * @param sentence 変換する文字列(かつ変換後の文字列を格納する配列)
 * @param sen_size sentenceの要素数
 * @param key 鍵
 * @param key_size 鍵の文字数(keyの要素数ではない)
 */
void vegenere(char mode, char *sentence, int sen_size, char *key, int key_size)
{
    int key_index = 0, i;
    for (i = 0; i < sen_size; i++)
        sentence[i] = tolower(sentence[i]);
    for (i = 0; i < key_size; i++)
        key[i] = tolower(key[i]);
    if (mode == 'm')
    {
        for (i = 0; i < sen_size; i++)
        {
            if (sentence[i] >= 'a' && sentence[i] <= 'z')
            {
                sentence[i] = 'A' + (sentence[i] + key[key_index] - 2 * 'a') % 26;
                key_index++;
            }
            if (key_index >= key_size)
                key_index = 0;
        }
    }
    if (mode == 'r')
    {
        for (i = 0; i < sen_size; i++)
        {
            if (sentence[i] >= 'a' && sentence[i] <= 'z')
            {
                int p = (sentence[i] - key[key_index]) % 26;
                if (p < 0)
                    p += 26;
                sentence[i] = 'a' + p;
                key_index++;
            }
            if (key_index >= key_size)
                key_index = 0;
        }
    }
}
/**
 * 単一換字式暗号を扱います。
 * @param mode "m":暗号化モード,"r":復号化モード
 * @param sentence 変換する文字列(かつ変換後の文字列を格納する配列)
 * @param sen_size sentenceの要素数
 * @param key 鍵(a-zに対応した文字列)
 */
void substitution(char mode, char *sentence, int sen_size, char *key)
{
    char abc[] = "abcdefghijklmnopqrstuvwxyz", *p;
    if (mode == 'm')
    {
        for (int i = 0; i < sen_size; i++)
        {
            if (sentence[i] >= 'a' && sentence[i] <= 'z')
            {
                p = strchr(abc, (int)sentence[i]);
                sentence[i] = key[p - abc];
            }
        }
    }
    if (mode == 'r')
    {
        for (int i = 0; i < sen_size; i++)
        {
            char keystr[26];
            for (int i = 0; i < 26; i++)
                keystr[i] = key[i];
            p = strchr(keystr, (int)sentence[i]);
            if (p - keystr < 26)
                sentence[i] = abc[p - keystr];
        }
    }
}
/**
 * @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';
    }
}
/**
 * @brief スキュタレー暗号を扱います。
 * 5列に分けて暗号化・復号化を行います。
 * sen_sizeは文字数にしてください。
 * return_arrayは少し余裕をもって宣言しておいたほうがいいです。
 * 言語の仕様上変数の値を配列の要素数の宣言に使用できなかったので、500個(499文字)までに対応させました。
 * Chipher.hのマクロSCYTALE_LENGTHを書き換えることによって最大文字数を変更できます。
 * SCYTALE_LENGTHは5の倍数にしてください。
 * @param mode "m":暗号化モード,"r":復号化モード
 * @param sentence 変換する文字列
 * @param sen_size sentenceの文字数
 * @param return_array 変換後の文字列を格納する配列
*/
void scytale(char mode,char* sentence,int sen_size,char* return_array){
    if(mode=='m'){
        int table[5][(int)(SCYTALE_LENGTH)/5];
        for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
        for(int i=0;i<sen_size;i++){
            table[i%5][i/5]=sentence[i];
        }
        int k=0;
        for(int i=0;i<5;i++){
            for(int j=0;table[i][j]!=0;j++){
                if(j>=(int)(SCYTALE_LENGTH)/5)break;
                return_array[k]=table[i][j];
                k++;
            }
        }
        return_array[k]='\0';
    }
    if(mode=='r'){
        int table[5][(int)(SCYTALE_LENGTH)/5];
        for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
        for(int i=0;i<sen_size;i++){
            table[i%5][i/5]=sentence[i];
        }
        int k=0;
        for(int i=0;i<5;i++){
            for(int j=0;table[i][j]!=0;j++){
                if(j>=(int)(SCYTALE_LENGTH)/5)break;
                if(k<sen_size)table[i][j]=sentence[k];
                k++;
            }
        }
        for(k=0;k<sen_size;k++){
            return_array[k]=table[k%5][k/5];
        }
        return_array[k]='\0';
    }
}
#endif

C++

/**
 * @file Cipher.hpp
 * @author PotatoTimeKun (https://github.com/PotatoTimeKun)
 * @brief 暗号を扱うクラスChipherが入っています。
 * 
 */
#ifndef CHIPHER_HPP_INCLUDED
#define CHIPHER_HPP_INCLUDED
#define SCYTALE_LENGTH 500
#include <iostream>
#include <algorithm>
#include <cctype>
#include <string>
using namespace std;
/**
 * 暗号を扱うstaticな関数が入っています。
 * 関数一覧
 * string ceaser(string mode,string sentence,int shift)
 * string vigenere(string mode,string sentence,string key)
 * string substitution(string mode,string sentence,string key)
 */
class Cipher{
    /**
     * シーザー暗号を扱います。
     * 
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @param shift シフトする数
     * @return string 暗号文または平文
     */
    public:static string ceaser(string mode,string sentence,int shift){
        string ret(sentence);
        for(int i=0;i<sentence.length();i++)sentence[i]=tolower(sentence[i]);
        if(mode[0]=='m'){
            if(shift<0)return ceaser("r",sentence,-shift);
            for(int i=0;i<sentence.length();i++){
                if(sentence[i]>='a' && sentence[i]<='z')ret[i]='A'+(sentence[i]-'a'+shift)%26;
                else ret[i]=sentence[i];
            }
        }
        if(mode[0]=='r'){
            if(shift<0)return ceaser("m",sentence,-shift);
            for(int i=0;i<sentence.length();i++){
                if(sentence[i]>='a' && sentence[i]<='z'){
                        int p=((int)sentence[i]-(int)'a'-shift)%26;
                        if(p<0)p+=26;
                        ret[i]='a'+p;
                }
                else ret[i]=sentence[i];
            }
        }
        return ret;
    };
    /**
     * ヴィジュネル暗号を扱います。
     * 
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @param key 鍵
     * @return string 暗号文または平文
     */
    public:static string vigenere(string mode,string sentence,string key){
        string ret(sentence);
        int i_k=0,sl=sentence.length(),kl=key.length();
        for(int i=0;i<sl;i++)sentence[i]=tolower(sentence[i]);
        for(int i=0;i<kl;i++)key[i]=tolower(key[i]);
        if(mode[0]=='m'){
            for(int i=0;i<sentence.length();i++){
                if(sentence[i]>='a' && sentence[i]<='z'){
                    ret[i]='A'+(sentence[i]+key[i_k]-2*'a')%26;
                    i_k++;
                }else{ret[i]=sentence[i];}
                if(i_k>=key.length())i_k=0;
            }
        }
        if(mode[0]=='r'){
            for(int i=0;i<sentence.length();i++){
                if(sentence[i]>='a' && sentence[i]<='z'){
                    int p=((int)sentence[i]-(int)key[i_k])%26;
                    if(p<0)p+=26;
                    ret[i]='a'+p;
                    i_k++;
                }else{ret[i]=sentence[i];}
                if(i_k>=key.length())i_k=0;
            }
        }
        return ret;
    };
    /**
     * 単一換字式暗号を扱います。
     * 
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @param key 鍵(a-zに対応した文字列)
     * @return string 暗号文または平文
     */
    public:static string substitution(string mode,string sentence,string key){
        string abc="abcdefghijklmnopqrstuvwxyz";
        string ret(sentence);
        if(mode[0]=='m'){
            for(int i=0;i<sentence.length();i++){
                try
                {
                    ret[i]=key[abc.find(sentence[i])];
                }
                catch(const std::exception& e)
                {
                    ret[i]=sentence[i];
                }
                
            }
        }
        if(mode[0]=='r'){
            for(int i=0;i<sentence.length();i++){
                try
                {
                    ret[i]=abc[key.find(sentence[i])];
                }
                catch(const std::exception& e)
                {
                    ret[i]=sentence[i];
                }
                
            }
        }
        return ret;
    }
    /**
     * @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;
    }
    /**
     * @brief スキュタレー暗号を扱います。
     * 5列に分けて暗号化します。
     * 言語の仕様上変数の値を配列の要素数の宣言に使用できなかったので、500個(499文字)までに対応させました。
     * Chipher.hppのマクロSCYTALE_LENGTHを書き換えることによって最大文字数を変更できます。
     * SCYTALE_LENGTHは5の倍数にしてください。
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @return string 暗号文または平文
     */
    public:static string scytale(string mode,string sentence){
        string ret="";
        int sen_size=sentence.length();
        if(mode[0]=='m'){
            int table[5][int(SCYTALE_LENGTH)/5];
            for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
            for(int i=0;i<sen_size;i++){
                table[i%5][i/5]=sentence[i];
            }
            for(int i=0;i<5;i++){
                for(int j=0;table[i][j]!=0;j++){
                    if(j>=int(SCYTALE_LENGTH)/5)break;
                    ret+=table[i][j];
                }
            }
        }
        if(mode[0]=='r'){
            int table[5][int(SCYTALE_LENGTH)/5];
            for(int i=0;i<SCYTALE_LENGTH;i++)table[i%5][i/5]=0;
            for(int i=0;i<sen_size;i++){
                table[i%5][i/5]=sentence[i];
            }
            int k=0;
            for(int i=0;i<5;i++){
                for(int j=0;table[i][j]!=0;j++){
                    if(j>=int(SCYTALE_LENGTH)/5)break;
                    if(k<sentence.length())table[i][j]=sentence[k];
                    k++;
                }
            }
            for(int i=0;i<sen_size;i++){
                ret+=table[i%5][i/5];
            }
        }
        return ret;
    }
};

#endif // CHIPHER_HPP_INCLUDED

Java

/**
 * Cipher.java
 * 暗号を扱います。
 * @author PotatoTimeKun (https://github.com/PotatoTimeKun)
 * 
 */

import java.util.List;
import java.util.stream.Collectors;

/**
 * 暗号を扱うクラスです。
 * シーザー暗号・ヴィジュネル暗号・単一換字式暗号に対応しています。
 * いずれもstaticな関数として入っています。
 */
public class Cipher {
    /**
     * シーザー暗号を扱います。
     * 
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @param shift シフトする数
     * @return String 暗号文または平文
     */
    static public String ceasar(String mode, String sentence, int shift) {
        sentence = sentence.toLowerCase();
        int a = 'a', z = 'z';
        List<Integer> list = sentence.chars().boxed().collect(Collectors.toList());
        String ret = "";
        if (mode == "m") {
            if (shift < 0)
                return ceasar("r", sentence, -shift);
            for (int i : list)
                if (i >= a && i <= z)
                    ret += Character.toString(a + (i - a + shift) % 26);
                else
                    ret += Character.toString(i);
            ret = ret.toUpperCase();
        }
        if (mode == "r") {
            if (shift < 0)
                return ceasar("m", sentence, -shift);
            for (int i : list) {
                if (i >= a && i <= z) {
                    int p = (i - a - shift) % 26;
                    if (p < 0)
                        p = 26 + p;
                    ret += Character.toString(a + p);
                } else
                    ret += Character.toString(i);
            }
        }
        return ret;
    }

    /**
     * ヴィジュネル暗号を扱います。
     * 
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @param key 鍵
     * @return String 暗号文または平文
     */
    static public String vigenere(String mode, String sentence, String key) {
        int a = 'a', z = 'z';
        key = key.toLowerCase();
        sentence = sentence.toLowerCase();
        List<Integer> list_sen = sentence.chars().boxed().collect(Collectors.toList());
        List<Integer> list_key = key.chars().boxed().collect(Collectors.toList());
        int b = 0;
        String ret = "";
        if (mode == "m") {
            for (int i : list_sen) {
                if (b == key.length())
                    b = 0;
                if (i >= a && i <= z)
                    ret += Character.toString(a + (i + list_key.get(b) - 2 * a) % 26);
                else
                    ret += Character.toString(i);
                b++;
            }
            ret = ret.toUpperCase();
        }
        if (mode == "r") {
            for (int i : list_sen) {
                if (b == key.length())
                    b = 0;
                if (i >= a && i <= z) {
                    if ((i - list_key.get(b)) % 26 >= 0)
                        ret += Character.toString(a + (i - list_key.get(b)) % 26);
                    else
                        ret += Character.toString(a + 26 + (i - list_key.get(b)) % 26);
                } else
                    ret += Character.toString(i);
                b++;
            }
        }
        return ret;
    }

    /**
     * 単一換字式暗号を扱います。
     * 
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @param key 鍵(a-zに対応した文字列)
     * @return String 暗号文または平文
     */
    static public String substitution(String mode, String sentence, String key) {
        String abc = "abcdefghijklmnopqrstuvwxyz";
        String ret = "";
        if (mode == "m") {
            for (int i = 0; i < sentence.length(); i++){
                try {
                    ret += key.charAt(abc.indexOf(sentence.charAt(i)));
                } catch (Exception e) {
                    ret+=sentence.charAt(i);
                }
            }
        }
        if (mode == "r") {
            for (int i = 0; i < sentence.length(); i++){
                try {
                    ret += abc.charAt(key.indexOf(sentence.charAt(i)));
                } catch (Exception e) {
                    ret+=sentence.charAt(i);
                }
            }
        }
        return ret;
    }

    /**
     * ポリュビオスの暗号表(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;
    }
    /**
     * スキュタレー暗号を扱います。
     * 5行に分けて暗号化します。
     *
     * @param mode "m":暗号化モード,"r":復号化モード
     * @param sentence 変換する文字列
     * @return String 暗号文または平文
     */
    public static String scytale(String mode,String sentence){
        String ret="";
        List<Integer> list_sen = sentence.chars().boxed().collect(Collectors.toList());
        int table[][]=new int[5][(sentence.length()+4)/5];
        for(int i=0;i<(sentence.length()+4)/5*5;i++)table[i%5][i/5]=0;
        for(int i=0;i<sentence.length();i++){
            table[i%5][i/5]=list_sen.get(i);
        }
        if(mode=="m"){
            for(int i=0;i<5;i++){
                for(int j=0;j<(sentence.length()+4)/5;j++){
                    if(table[i][j]!=0)ret+=Character.toString(table[i][j]);
                }
            }
        }
        if(mode=="r"){
            int k=0;
            for(int i=0;i<5;i++){
                for(int j=0;j<(sentence.length()+4)/5;j++){
                    if(k<sentence.length() && table[i][j]!=0)table[i][j]=list_sen.get(k);
                    k++;
                }
            }
            for(int i=0;i<sentence.length();i++){
                ret+=Character.toString(table[i%5][i/5]);
            }
        }
        return ret;
    }
}

Python

#Cipher.py
class Cipher:
    """
    暗号を扱うクラスです
    """
    def ceasar(mode:str,sentence:str,shift:int)->str:
        """
        シーザー暗号を扱います。
        mode "m":暗号化モード,"r":復号化モード
        sentence 変換する文字列
        shift シフトする数
        ->return 暗号文または平文
        """
        sentence=sentence.lower()
        a=ord('a')
        z=ord('z')
        ret=""
        if(mode=="m"):
            if(shift<0):return Cipher.ceasar("r",sentence,shift)
            for i in sentence:
                if(ord(i)>=a and ord(i)<=z):
                    ret+=chr(a+(ord(i)-a+shift)%26)
                else:ret+=i
            ret=ret.upper()
        if(mode=="r"):
            if(shift<0):return Cipher.ceasar("m",sentence,shift)
            for i in sentence:
                if(ord(i)>=a and ord(i)<=z):
                    p=(ord(i)-a-shift)%26
                    if(p<0):p+=26
                    ret+=chr(a+p)
                else:ret+=i
            ret=ret.lower()
        return ret
    def vigenere(mode:str,sentence:str,key:str)->str:
        """
        ヴィジュネル暗号を扱います。
        mode "m":暗号化モード,"r":復号化モード
        sentence 変換する文字列
        key 鍵
        ->return 暗号文または平文
        """
        a=ord('a')
        z=ord('z')
        key=key.lower()
        sentence=sentence.lower()
        b=0
        ret=""
        if(mode=="m"):
            for i in sentence:
                if b==len(key):
                    b=0
                if(i>=a and i<=z):
                    ret+=chr(a+(ord(i)+ord(key[b])-2*a)%26)
                else :
                    ret+=i
                b+=1
                ret=ret.upper()
        if(mode=="r"):
            for i in sentence:
                if b==len(key):
                    b=0
                if(i>=a and i<=z):
                    if((ord(i)-ord(key[b])%26>=0)):
                        ret+=chr(a+(ord(i)-ord(key[b])%26))
                    else:
                        ret+=chr(a+26+(ord(i)-ord(key[b])%26>=0))
                else :
                    ret+=i
                b+=1
                ret=ret.lower()
        return ret
    def substitution(mode:str,sentence:str,key:str)->str:
        """
        単一換字式暗号を扱います。
        mode "m":暗号化モード,"r":復号化モード
        sentence 変換する文字列
        key 鍵(a-zに対応した文字列)
        ->return 暗号文または平文
        """
        abc = "abcdefghijklmnopqrstuvwxyz"
        ret=""
        if mode=="m":
            for i in range(0,len(sentence)):
                try:
                    ret+=key[abc.index(sentence[i])]
                except:
                    ret+=sentence[i]
        if mode=="r":
            for i in range(0,len(sentence)):
                try:
                    ret+=abc[key.index(sentence[i])]
                except:
                    ret+=sentence[i]
        return ret
    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
    def scytale(mode:str,sentence:str)->str:
        """
        スキュタレー暗号を扱います。
        5列に分けて暗号化します。
        mode "m":暗号化モード,"r":復号化モード
        sentence 変換する文字列
        ->return 暗号文または平文
        """
        ret=""
        if(mode=="m"):
            table=[[0,0,0,0,0]]
            for i in range(0,len(sentence)):
                if(len(table)<=int((i)/5)):table.append([0,0,0,0,0])
                table[int(i/5)][i%5]=ord(sentence[i])
            for i in range(0,5):
                for j in range(0,len(table)):
                    if(table[j][i]!=0):ret+=chr(table[j][i])
        if(mode=="r"):
            table=[[0,0,0,0,0]]
            for i in range(0,len(sentence)):
                if(len(table)<=int((i)/5)):table.append([0,0,0,0,0])
                table[int(i/5)][i%5]=ord(sentence[i])
            k=0
            for i in range(0,5):
                for j in range(0,len(table)):
                    if(k<len(sentence) and table[j][i]!=0):
                        table[j][i]=ord(sentence[k])
                        k+=1
            for i in range(0,len(sentence)):
                if(table[int(i/5)][i%5]!=0):ret+=chr(table[int(i/5)][i%5])
        return ret

このシリーズを書き始める時点ではまだなかったDartのプログラムもあるので、次回の記事はそれを紹介します。

まとめ編と言いつつも、また別の暗号化方式を実装するときはそのとき記事にします。多分。

この記事が気に入ったらサポートをしてみませんか?