CS50 Probelm set 2 - 치환 암호

dondonee·2023년 2월 3일
0

CS50

목록 보기
7/12
post-thumbnail

치환 암호

치환 암호는 26자리의 알파벳 배열을 암호키로 사용한다. A부터 Z까지 알파벳 문자를 순서대로 암호키에 대응시키고 평문의 각 문자를 그에 상응하는 것으로 치환하여 암호화하는 방식이다. 예를 들어 NQXPOMAFTRHLZGECYJIUWSKDVB라는 문자열을 암호키로 사용한다면, 평문의 A를 암호화 하면 N이 된다.


Substitution 과제

명령행 인자를 통해 사용자에게 26자리 알파벳 배열을 암호키로 입력받고, 프롬프트로 평문을 입력받아 암호키를 사용해 암호화하여 출력하는 프로그램을 만든다.

$ ./substitution JTREKYAVOGDXPSNCUIZLFBMWHQ
plaintext:  HELLO
ciphertext: VKXXN

콤마나 공백 등의 문자는 변환하지 않는다. 알파벳 문자만 변환하여야 하며, 소문자는 소문자로, 대문자는 대문자로 변환한다.

$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOI
plaintext:  hello, world
ciphertext: jrssb, ybwsp

지시사항

  • 프로그램은 하나의 명령행 인자를 받아 암호키로 사용한다. 암호키는 대소문자를 구별하지 않고, 대문자인지 소문자인지는 프로그램의 동작에 영향을 끼치지 않는다.
  • 만약 프로그램이 어느 인자도 없이 실행되거나, 또는 하나 이상의 인자를 입력받는다면 에러메세지를 출력하고 main 함수가 즉시 1을 반환하도록 한다.
  • 암호키로 입력된 값이 유효하지 않은 경우 에러메세지를 출력하고 main 함수가 즉시 1을 반환하도록 한다. 암호키는 26개의 알파벳 문자로 이루어진 문자열이며, 알파벳 문자는 각각 한번씩 사용되어야 한다.
  • 프로그램은 plaintext:를 줄바꿈 없이 출력하고, 유저에게 string 타입의 인풋을 받는 프롬프트를 출력한다. (get_string 사용)
  • 프로그램은 ciphertext:를 줄바꿈 없이 출력하고, 평문의 각 알파벳 문자를 그에 상응하는 암호키의 문자로 치환한 암호문을 출력한다. 알파벳이 아닌 문자는 변환하지 않고 출력한다.
  • 대문자는 대문자로만 변환되고, 소문자는 소문자로만 변환된다.
  • 암호문을 출력한 뒤, 줄바꿈 문자를 출력한다. 그리고 main 함수로부터 0을 반환하고 프로그램을 종료한다.

✍️ 풀이

명령행 인자 유효성 체크

과제 안내문의 Specifications에는 없지만 Walkthorugh에 상황별 에러메세지를 출력하라는 안내가 있었기 때문에 명령행인자의 오입력 케이스를 여러 개로 구분했다.

bool checkArg(int argc, char **argv)
{

    if (argc != 2)
    {
        printf("usage: ./caesar key\n");
        return false;
    }

    if (strlen(argv[1]) != ALPHABET_LEN)
    {
        printf("Key must contain 26 characters.\n");
        return false;
    }

    char temp[ALPHABET_LEN];

    for (int i = 0; i < ALPHABET_LEN; i++)
    {
        temp[i] = toupper(argv[1][i]);
    }

    if (strspn(temp, "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != ALPHABET_LEN)
    {
        printf("Key must only contain alphabetic characters.\n");
        return false;
    }

    if (strspn("ABCDEFGHIJKLMNOPQRSTUVWXYZ", temp) != ALPHABET_LEN)
    {
        printf("Key must not contain repeated characters.\n");
        return false;
    }

    return true;
}
  1. key 값이 단일한지 체크
  2. key 값이 26자리 문자열인지 체크
  3. 임시 변수 temp에 argv[1]을 대문자로 변환하여 저장
    • key는 대소문자 구분이 없으므로 비교를 위해 변환
    • toupper()는 소문자 알파벳인 경우 대문자로 변환해 반환하고, 나머지는 그대로 반환.
  4. key 값이 알파벳 문자로만 구성되어 있는지 체크
  5. key 값으로 각 알파벳 문자를 1번씩 사용했는지 체크

문자 암호화

char *getCiphertext(char *key, char *plaintext)
{
    size_t len = strlen(plaintext);
    char *result = malloc(len);

    for (int i = 0; i < (int)len; i++)
    {
        char character = plaintext[i];
        int index;

        if (isupper(character))
        {
            index = character - 'A';
            result[i] = toupper(key[index]);
        }
        else if (islower(character))
        {
            index = character - 'a';
            result[i] = tolower(key[index]);
        }
        else
        {
            result[i] = character;
        }
    }

    return result;
}
  1. plantext 길이와 같은 result 문자열 변수를 만든다.
  2. for문으로 각 plaintext의 문자를 0번째부터 체크한다.
    • 문자가 대문자인지 소문자인지 구분한다(ASCII 표를 이용해야 하므로).
    • 문자가 A~Z에서 몇 번째 알파벳인지 index를 구한다.
    • Key 문자열에서 index에 대응하는 암호 문자를 찾아 result에 저장한다.
    • 반복한다.
  3. 문자열을 반환한다.

main()

#define ALPHABET_LEN 26

bool checkArg(int argc, char **argv);
char *getCiphertext(char *key, char *plaintext);

int main(int argc, char **argv)
{
    if (!checkArg(argc, argv))
    {
        return 1;
    }

    char *plaintext = get_string("plaintext:  ");
    char *ciphertext = getCiphertext(argv[1], plaintext);

    printf("ciphertext: %s", ciphertext);
    printf("\n");
    free(ciphertext);

    return 0;
}


✍️ 메모

Size_t 타입

  • strlen()의 리턴 타입.
  • 부호 없는 정수 타입. 이론적으로 가능한 모든 객체의 최대 크기를 저장할 수 있다. 16비트 이상의 크기를 가진다(C99). 32bit 운영체제에서는 4byte, 64bit 운영체제에서는 8byte이다.
  • 객체의 크기를 저장하는 타입으로 unsigned int를 사용할 경우 오버플로우 될 수 있다.
  • 주로 배열 인덱싱 및 루프 카운팅에 사용된다.
  • %zu 형식 지정자 사용. %lu도 가능.



References

0개의 댓글