Пароль не будет правильно шифроваться (переносит слишком много символов)

Я пишу программу паролей. Программа начинается с запроса пользователя на ввод пароля, который включает 8 или более символов, включая символ и знак препинания.

После этого программа «зашифрует» пароль, перемещая вторую половину пароля вперед, добавляя «+» в середине, а затем добавляя первую половину в конец.

Проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я ввожу пароль, состоящий из нечетных символов, он дважды копирует последний символ и ломает мою программу. Как это исправить? Я знаю, что мне нужно добавить условие, если есть нечетные символы, но не знаю, что оно должно включать. Вот мой код:

// write a function to encrypt the password:
// return a dynamically allocated C string of the exact size (no extra room) with:
// the last half of the password, the + symbol, and then the first half of the password
char * encrypt (char pw[])
 {
int exactsize = strlen(pw) + 2;
char * encPW = new char[exactsize];
int mid = exactsize / 2;


strcpy(encPW, pw + mid);
strcat(encPW, "+");
strncat(encPW, pw, exactsize - mid);

return encPW;

 }

 // write the same (overloaded) function for a C++ String
 char * encrypt (string pw)
 {
int exactsize = pw.length() + 2;
char * encPW = new char[exactsize];
int mid = exactsize / 2;

string temp(pw, mid);
string temp2(pw, 0 ,mid);
temp = temp + "+" + temp2;

strcpy(encPW, temp.c_str());


return encPW;
}

here is the output i am getting and what i am talking about:
Enter your password: salehrocks93
add punctuation
Enter your password: salehrocks93/
Valid password format. Encrypting...

Encrypted password: cks93/+salehroc

Enter your new password: salehrocks93/
New Encrypted password: cks93/+salehro
1
Press any key to continue . . .

когда он запрашивает пароль в первый раз и шифрует его, c повторяется дважды


person SalehK    schedule 28.01.2014    source источник
comment
Каково желаемое поведение, когда пароль нечетной длины? Например, каким должно быть шифрование «паролей»?   -  person crockeea    schedule 28.01.2014
comment
Несколько замечаний без ответа: 1. Ваш API несбалансирован: функция выделяет память, но требует, чтобы вызывающая сторона освободила ее - это плохая практика. 2. Ваша перегрузка принимает std::string по значению — это означает, что она создает локальную копию при вызове, что делает ее использование в качестве перегрузки плохим выбором дизайна. Сделайте это const std::string&, чтобы взять его по ссылке. 3. temp = temp + "+" + temp2; подумайте о том, что это делает...   -  person kfsone    schedule 28.01.2014
comment
@ Эрик, шифрование паролей всегда должно быть одинаковым, возьмите вторую половину пароля, поместите ее перед первой и добавьте + в середине.   -  person SalehK    schedule 28.01.2014
comment
@kfsone У меня проблема только с моей первой функцией, когда я делаю это в строке C, строка C++ не доставляет мне никаких проблем   -  person SalehK    schedule 28.01.2014


Ответы (2)


Что мне нравится делать с такими проблемами? Пройдитесь по ним. Я считаю, что это часто помогает сделать выводы. Старая добрая отладка с помощью ручки и бумаги. Если вы не можете понять это на бумаге, как вы поймете это на компьютере?! Столько проводов!

Так. Давайте пройдем через это. Сначала нам нужно выбрать поддельный аргумент, чтобы передать его.


Для целей этой демонстрации давайте воспользуемся строкой в ​​стиле C, заканчивающейся NULL, "PASSWOR". Если бы я рисовал этот массив, он мог бы выглядеть так:

['P'].['A'].['S'].['S'].['W'].['O'].['R']

Хорошо, теперь о вашем коде.

char * encrypt (char pw[])
 {
  int exactsize = strlen(pw) + 2;
  char * encPW = new char[exactsize];
  int mid = exactsize / 2;

Вот мы и в начале вашей функции. Вы объявили переменную exactsize, используя длину строки и магическое число. Я предполагаю, что вы используете это магическое число для учета терминатора NULL и знака «+», который вы хотите добавить.

Таким образом, с нашим поддельным аргументом «ПАРОЛЬ» мы должны получить strlen() 7, что делает наш «точный размер» 9.

Затем вы выделяете массив символов encPW в качестве этого нового «точного» размера. Вот как теперь выглядит encPW в виде массива символов

// initial value
[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ]
// with indices
  0     1     2     3     4     5     6     7     8

Затем вы объявляете mid, что, я думаю, мы можем с уверенностью предположить, означает середину. Это немного сложно, потому что вы определяете середину по отношению к вашему новому размеру. Но давайте придерживаться кода под рукой. Поскольку наш exactsize равен 9, средний должен быть 4,5. Но это целое число, поэтому конечный результат равен 4. Мы продолжаем.

strcpy(encPW, pw + mid);

Теперь вы выполняете strcpy в место назначения encPW, начиная с pw + mid. pw + mid дает нам указатель на символ 'W' из исходной строки. Он будет копироваться вплоть до терминатора NULL включительно.

// now what do we have?
['W'].['O'].['R'].[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ]

Затем вы добавляете '+' к строке:

strcat(encPW, "+");
// now what do we have?
[ 'W' ].[ 'O' ].[ 'R' ].[ '+' ].[ 0 ].[ 0 ].[ 0 ].[ 0 ].[ 0 ]

Наконец, вы вызываете strncat(), которая добавляет число символов к строке в стиле C, плюс разделитель NULL.

Вы говорите ему записать в строку exactsize - mid символов. Так как exactsize равен 9, а mid равен 4, мы говорим ему писать 5 символов.

Как мы видели выше, strncat также записывает терминатор NULL. Это 6 символов! Однако, как мы видим (подсчитав 0 в приведенной выше строке), осталось всего 5 символов, прежде чем мы начнем запись в чужую память!

Вот что происходит под капотом (на высоком уровне)

strncat(encPW, pw, exactsize - mid)
// strncat tries to write this stuff at the end
[ 'W' ].[ 'O' ].[ 'R' ].[ '+' ].[ 'P' ].[ 'A' ].[ 'S' ].[ 'S' ].[ 'W' ].[ 0 ]
                                                                         .^
                                      Uh oh, somebody call the memory police!

Однако, даже если бы мы не записывались в страшную, загадочную память, алгоритм все равно кажется в корне ошибочным, потому что один из наших персонажей появляется дважды!


Мой вопрос к вам: что такое мид? Как вы намеревались использовать его, когда объявили об этом?

Итак, теперь мы придумали наше простое решение с ручкой и бумагой.


Как бы я решил это:

int mid = strlen(pw) / 2;

И для вашего strncat() я бы использовал

strncat(encPW, pw, mid)

Это решает несколько проблем для нас. Давайте используем два примера: «ПАРОЛЬ» и «ПАРОЛЬ».

При использовании PASSWOR mid будет равен 3

pw + mid будет 'S' (вторая s)

После strcpy encPW будет "SWOR"

После первого strcat будет "SWOR+"

После strncat(encPW, pw, mid) будет "SWOR+PAS".

Идеал, да?

При использовании PASSWORD mid будет равен 4

pw + mid будет 'W'

После strcpy encPW будет "WORD"

После первого strcat будет "WORD+"

После strncat(encPW, pw, mid) будет "WORD+PASS".

person Robert Kelly    schedule 28.01.2014

#include <iostream>
#include <string>

void encrypt(const std::string& password)
{
    size_t exactSize = password.length() + sizeof('+') + sizeof('\0');
    size_t mid = exactSize / 2;

    std::cout << "password = " << password
              << ", exactSize = " << exactSize
              << ", mid = " << mid
              << ", password[0,mid) = " << password.substr(0, mid)
              << ", password[mid,npos) = " << password.substr(mid, std::string::npos)
              << '\n';

    std::string t1(password, mid);
    std::string t2(password, 0, mid);

    std::cout << "t1 = " << t1 << ", t2 = " << t2 << '\n';
}

int main()
{
    encrypt("12345678");
    encrypt("123456789");

    return 0;
}

Производит выходные данные http://ideone.com/fork/Y9GsHM.

password = 12345678,  exactSize = 10, mid = 5, password[0,mid) = 12345, password[mid,npos) = 678
t1 = 678, t2 = 12345
password = 123456789, exactSize = 11, mid = 5, password[0,mid) = 12345, password[mid,npos) = 6789
t1 = 6789, t2 = 12345

Проблема в том, что exactSize – это размер результата, включая "+" и "\0", а не размер исходной строки.

#include <iostream>
#include <string>

void encrypt(const std::string& password)
{
    size_t mid = password.length() / 2;
    size_t exactSize = password.length() + sizeof('+') + sizeof('\0');

    std::cout << "password = " << password
              << ", exactSize = " << exactSize
              << ", mid = " << mid
              << ", password[0,mid) = " << password.substr(0, mid)
              << ", password[mid,npos) = " << password.substr(mid, std::string::npos)
              << '\n';

    std::string t1(password, mid);
    std::string t2(password, 0, mid);

    std::cout << "t1 = " << t1 << ", t2 = " << t2 << '\n';
}

int main()
{
    encrypt("12345678");
    encrypt("123456789");

    return 0;
}

http://ideone.com/E5YFKm

Ваша версия C-строки тоже довольно хитрая, я сделал соответствующую версию C-строки приведенного выше кода.

#include <iostream>
#include <cstring>

void encrypt(const char* password)
{
    size_t len = strlen(password);
    size_t allocSize = strlen(password) + sizeof('+') + sizeof('\0');
    size_t mid = len / 2;

    char left[16];
    char right[16];

    strncpy(left, password, len - mid);
    left[len - mid] = '\0';
    strcpy(right, password + mid);

    std::cout << "password = " << password
              << ", len = " << len
              << ", remainder = " << (len - mid)
              << ", mid = " << mid
              << ", left = " << left
              << ", right = " << right
              << '\n';
}

int main()
{
    std::cout << "begin\n";
    encrypt("12345678");
    encrypt("123456789");

    return 0;
}

http://ideone.com/O6Na9t

person kfsone    schedule 28.01.2014