Шифр Виженера — необъяснимый нюанс

#c #algorithm #encryption #cs50 #vigenere

#c #алгоритм #шифрование #cs50 #виженер

Вопрос:

Я реализую шифр Виженера в C. Мое решение продолжало неправильно шифровать обычный текст. Поэтому я решил внести (несколько произвольное) изменение в свой код. Целью изменения было просто сделать код более читаемым, улучшив расположение моих переменных и присвоив им более подходящие имена. Однако это изменение теперь привело к правильному шифрованию решения; Я не могу объяснить, почему текущее решение работает поверх исходного. Может кто-нибудь, возможно, просветит меня?

оригинал.c

 //...code to validate user input

string k = argv[1]; //'argv' being a main method parameter
string str = GetString();

//Encrypt using vigenere
for (int i = 0, n = strlen(str); i < n; i  ) {

    /*An int to keep track of which char to use from the keyword.
    Only increment if str[i] was alphabetic.*/
    int k_index = 0;
    int k_len = strlen(k);
    char letter_key = tolower(k[k_index % k_len]);

    //Checking if str[i] is alphabetic
    if (isalpha(str[i])) {

        //Checking if str[i] is uppercase
        if (isupper(str[i])) {

            //enciphering using vigenere formula
            str[i] = ((str[i] - 'A')   (letter_key - 'a')) % 26   'A';
            printf("%c", str[i]);
            k_index  ;
        }

        //If not uppercase, it must be lowercase
        else {
            //enciphering using vigenere formula
            str[i] = ((str[i] - 'a')   (letter_key - 'a')) % 26   'a';
            printf("%c", str[i]);
            k_index  ;
        }

    } else {
        printf("%c", str[i]);
    }
}
 

Вывод

 Key: "chasi"
Input/Result: "my plaintext" ---> "oa rnckpvgzv" 
Should be: "of pdikutwfv"
 

обновлено.c

 //...code to validate user input

string k = argv[1];
string str = GetString();

//Encrypt using vigenere
for (int i = 0, j = 0, n = strlen(str); i < n; i  ) {

    /*"int j" is to keep track of which char to use from the keyword.
    Only increment if str[i] was alphabetic.*/

    int k_len = strlen(k);
    char letter_key = tolower(k[j % k_len]) - 'a';

    //Checking if str[i] is alphabetic
    if (isalpha(str[i])) {

        //Checking if str[i] is uppercase
        if (isupper(str[i])) {

            //enciphering using vigenere formula
            str[i] = ((str[i] - 'A')   letter_key) % 26   'A';
            printf("%c", str[i]);
            j  ;
        }

        //If not uppercase, it must be lowercase
        else {
            //enciphering using vigenere formula
            str[i] = ((str[i] - 'a')   letter_key) % 26   'a';
            printf("%c", str[i]);
            j  ;
        }

    } else {
        printf("%c", str[i]);
    }
}
 

Вывод:

 Key: "chasi"
Input/Result: "my plaintext" ---> "of pdikutwfv" (correct)
 

Комментарии:

1. @user3386109 его исправили

2. В исходном коде k_index устанавливается обратно 0 каждый раз через цикл. Обновленный код устанавливает его 0 в начале, затем увеличивает его во время циклов.

3. @Barmar аааа, я вижу! Вот почему, по крайней мере, первый символ всегда правильный в исходном коде!

4. Переменная — не единственное изменение. Вы также изменили строки «шифрования».

5. @WeatherVane да, но математика, стоящая за строками шифрования, все та же. Просто вычисление ‘letter_key’ выполняется ранее.

Ответ №1:

Исходный код не:

 int k_index = 0;
 

каждый раз через цикл. Затем, когда это произойдет:

 char letter_key = tolower(k[k_index % k_len]);
 

он использует это 0 значение, так letter_key всегда tolower(k[0]) . Места, где это не k_index ; имеет никакого эффекта, потому что переменная обнуляется до того, как она будет использована снова. Итак, вы просто используете первый символ ключа как весь ключ.

В обновленном коде переменная j занимает место k_index . Он инициализируется 0 при запуске цикла, а не каждый раз в цикле. Поэтому, когда вы делаете:

 char letter_key = tolower(k[j % k_len]) - 'a';
 

вы используете обновленное значение j . При этом правильно используется весь ключ.