[보안] A5/1 구현(C++)

A5/1 구현(C++)

Stream Cipher 중 하나인 A5/1를 C++로 구현하고 암호화와 복호화를 해보았습니다.

구현 과정

키 구현 방식 입력받기

printf("------------------------------------------------------------------\n");
printf("-----------------------------A5/1 구현------------------------------\n");
printf("키를 직접 입력하시겠습니까?(입력하실꺼면 1, 랜덤한 키는 2번) -> ");
cin >> ans;
while(true){
    if(ans==1){
        char str[65];
        cin >> str;
        for(int i=0;i<64;i++){
            key[i]=str[i]-'0';
        }
        break;
    }
    else if(ans==2){
        srand((unsigned  int)time(0));
        for(int i=0;i<64;i++){
            key[i] = rand()%2;
        }
        cout << "랜덤하게 생성된 키 값은 ";
        for(int i=0;i<64;i++) cout << key[i];
        cout << " 입니다!\n";
        break;
    }
    else{
        printf("잘 못 입력하셨습니다. 다시 입력해주세요! -> ");
        cin >> ans;
        continue;
    }
}

우선 사용자가 키를 직접 입력하거나 랜덤하게 생성 된 키를 받는 것을 입력을 통해 정할 수 있도록 했습니다.
1번을 입력하면 키를 입력할 수 있고 2번을 입력하면 랜덤 한 키를 받을 수 있습니다.
만 약 1이나 2가 아닌 다른 수가 입력된다면 올바른 입력 값을 받을 때까지 입력을 받을 수 있게 했습니다.

평문 입력받기

cout << "평문을 입력해주세요! ";
char empty[2];
cin.getline(empty,2,'\n');
cin.getline(pText,1000,'\n');
sizeVal=strlen(pText);
int bitCnt=0;
for(int i=0;i<sizeVal;i++){
    for(int j=7;j>=0;j--){
        pTextBool[bitCnt] = bitset<8>(pText[i])[j];
        bitCnt++;
    }
}
sizeVal*=8;
makeKeySteam();

평문을 일단 문자열로 입력받은 뒤, bool형 평문 배열을 만들어줍니다.
문자열 하나하나를 bitset 함수를 이용해 8비트로 변환해 주고 bool 형 평문 배열에 저장합니다.
그 뒤에 키스트림을 만드는 함수를 호출합니다.

키스트림 생성 함수

void makeKeySteam(){
    int cnt = 0;
    int st =0;
    for(int i=0;i<64;i++){
        if(i==19 || i==41) cnt=0;
        if(i<19) x[cnt]=key[i];
        else if(i<41) y[cnt]=key[i];
        else z[cnt] = key[i];
        cnt++;
    }
    cnt=sizeVal;
    while(cnt--) {
        int maj = chkMaj(x[8], y[10], z[10]);
        if (x[8] == maj) excuteX();
        if (y[10] == maj) excuteY();
        if (z[10] == maj) excuteZ();
        keyStream[st] = x[18]^y[21]^z[22];
        st++;
    }
}

키스트림을 만드는 함수입니다.
우선 위에서 생성된 키값을 x,y,z에 분배를 해줍니다.
그 뒤에 다수결 함수를 이용해 다수결 수를 결정하고 다수결 수를 확인하며 x,y,z를 확인해 주고 다수결 함수와 각 해당 비트가 일치한다면 각각에서 함수를 불러 변환하는 과정을 실행합니다.

maj 확인, excute X,Y,Z

bool chkMaj(bool x, bool y, bool z){
    int arr[2] = {0,0};
    arr[x]++;
    arr[y]++;
    arr[z]++;
    if(arr[1]>arr[0]) return true;
    return false;
}

void excuteX(){
    bool tempZero = x[13] ^  x[16] ^ x[17] ^ x[18];
    for(int i=19;i>0;i--){
        x[i]=x[i-1];
    }
    x[0]=tempZero;
}
void excuteY(){
    bool tempZero = y[20]^y[21];
    for(int i=21;i>0;i--){
        y[i] = y[i-1];
    }
    y[0] = tempZero;
}
void excuteZ(){
    bool tempZero = z[7]^z[20]^z[21]^z[22];
    for(int i=22;i>0;i--){
        z[i]=z[i-1];
    }
    z[0]=tempZero;
}

maj를 확인하는 함수와 x,y,z에서 변환하는 함수입니다.

키스트림 출력

cout << "생성된 키스트림은 ";
for(int i=0;i<sizeVal;i++){
    cout << keyStream[i];
}
cout << " 입니다.\n\n";

위의 makeKeyStream을 통해 생성된 키스트림을 출력하여 콘솔 창에서 보여줍니다.

암호화 된 이진수, 문자 출력

cout << "키스트림으로 암호화를 한 이진수는 ";
for(int i=0;i<sizeVal;i++){
    cTextBool[i]=pTextBool[i]^keyStream[i];
    cout << cTextBool[i];
}
cout << " 입니다.\n";
cout << "글자가 깨질 수도 있지만 암호화로 된 이진수를 문자로 바꾼다면 ";
bitCnt= 0;
int now = 0;
for(int i=0;i<sizeVal;i++){
    now+=cTextBool[i]*pow(2,7-bitCnt);
    bitCnt++;
    if(bitCnt==8){
        bitCnt=0;
        char pr = now;
        cout << pr;
        now=0;
    }
}
cout << "입니다.\n";

키스트림으로 암호화 한 이진수를 구하기 위해 위에서 만든 bool 평문 배열과 키스트림을 xor 연산하여 보여줍니다.
또 한 해당 배열을 8비트 기준으로 문자열로 변환하는 과정을 보여주기 위해 8비트 씩 나눠서 각 자리에 맞는 수를 곱해 아스키코드로 출력합니다.

복호화 문자 출력

cout << "이를 다시 복호화 한다면 ";
bitCnt=0;
now=0;
for(int i=0;i<sizeVal;i++){
    finalTextBool[i]=cTextBool[i]^keyStream[i];
    now+=finalTextBool[i]*pow(2,7-bitCnt);
    bitCnt++;
    if(bitCnt==8){
        bitCnt=0;
        char pr = now;
        cout << pr;
        now=0;
    }
}
cout << " 입니다.\n";

복호화 하는 과정도 마찬가지로 위에서 만들었던 암호화된 이진수에 키스트림을 xor 연산하여 다시 복호화 합니다.
또 이 복호화 한 배열을 다시 8비트 기준으로 계산하여 아스키코드로 출력해 줍니다.

실행화면

1

전체 소스코드

#include <iostream>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <cstring>
#include <bitset>
using namespace std;
int keyStream[8001];
int ans;
bool key[64];
bool x[19];
bool y[22];
bool z[23];
char pText[8001];
bool pTextBool[8001];
bool cTextBool[8001];
bool finalTextBool[8001];
void excuteX();
void excuteY();
void excuteZ();
int sizeVal;
void makeKeySteam();
bool chkMaj(bool x,bool y, bool z);
int main(){
    printf("-----------------------------------------------------------------------------------------------------------\n");
    printf("--------------------------------------------------A5/1 구현--------------------------------------------------\n");
    printf("키를 직접 입력하시겠습니까?(입력하실꺼면 1, 랜덤한 키는 2번) -> ");
    cin >> ans;
    while(true){
        if(ans==1){
            char str[65];
            cin >> str;
            for(int i=0;i<64;i++){
                key[i]=str[i]-'0';
            }
            break;
        }
        else if(ans==2){
            srand((unsigned  int)time(0));
            for(int i=0;i<64;i++){
                key[i] = rand()%2;
            }
            cout << "랜덤하게 생성된 키 값은 ";
            for(int i=0;i<64;i++) cout << key[i];
            cout << " 입니다!\n";
            break;
        }
        else{
            printf("잘 못 입력하셨습니다. 다시 입력해주세요! -> ");
            cin >> ans;
            continue;
        }
    }
    cout << "평문을 입력해주세요! ";
    char empty[2];
    cin.getline(empty,2,'\n');
    cin.getline(pText,1000,'\n');
    sizeVal=strlen(pText);
    int bitCnt=0;
    for(int i=0;i<sizeVal;i++){
        for(int j=7;j>=0;j--){
            pTextBool[bitCnt] = bitset<8>(pText[i])[j];
            bitCnt++;
        }
    }
    sizeVal*=8;
    makeKeySteam();
    cout << "생성된 키스트림은 ";
    for(int i=0;i<sizeVal;i++){
        cout << keyStream[i];
    }
    cout << " 입니다.\n\n";
    cout << "키스트림으로 암호화를 한 이진수는 ";
    for(int i=0;i<sizeVal;i++){
        cTextBool[i]=pTextBool[i]^keyStream[i];
        cout << cTextBool[i];
    }
    cout << " 입니다.\n";
    cout << "글자가 깨질 수도 있지만 암호화로 된 이진수를 문자로 바꾼다면 ";
    bitCnt= 0;
    int now = 0;
    for(int i=0;i<sizeVal;i++){
        now+=cTextBool[i]*pow(2,7-bitCnt);
        bitCnt++;
        if(bitCnt==8){
            bitCnt=0;
            char pr = now;
            cout << pr;
            now=0;
        }
    }
    cout << "입니다.\n";

    cout << "이를 다시 복호화 한다면 ";
    bitCnt=0;
    now=0;
    for(int i=0;i<sizeVal;i++){
        finalTextBool[i]=cTextBool[i]^keyStream[i];
        now+=finalTextBool[i]*pow(2,7-bitCnt);
        bitCnt++;
        if(bitCnt==8){
            bitCnt=0;
            char pr = now;
            cout << pr;
            now=0;
        }
    }
    cout << " 입니다.\n";

}
void makeKeySteam(){
    int cnt = 0;
    int st =0;
    for(int i=0;i<64;i++){
        if(i==19 || i==41) cnt=0;
        if(i<19) x[cnt]=key[i];
        else if(i<41) y[cnt]=key[i];
        else z[cnt] = key[i];
        cnt++;
    }
    cnt=sizeVal;
    while(cnt--) {
        int maj = chkMaj(x[8], y[10], z[10]);
        if (x[8] == maj) excuteX();
        if (y[10] == maj) excuteY();
        if (z[10] == maj) excuteZ();
        keyStream[st] = x[18]^y[21]^z[22];
        st++;
    }
}

bool chkMaj(bool x, bool y, bool z){
    int arr[2] = {0,0};
    arr[x]++;
    arr[y]++;
    arr[z]++;
    if(arr[1]>arr[0]) return true;
    return false;
}

void excuteX(){
    bool tempZero = x[13] ^  x[16] ^ x[17] ^ x[18];
    for(int i=19;i>0;i--){
        x[i]=x[i-1];
    }
    x[0]=tempZero;
}
void excuteY(){
    bool tempZero = y[20]^y[21];
    for(int i=21;i>0;i--){
        y[i] = y[i-1];
    }
    y[0] = tempZero;
}
void excuteZ(){
    bool tempZero = z[7]^z[20]^z[21]^z[22];
    for(int i=22;i>0;i--){
        z[i]=z[i-1];
    }
    z[0]=tempZero;
}